@sumicom/quicksave 0.8.6 → 0.8.7

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 (188) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +1 -1
  3. package/dist/ai/asyncQueue.d.ts.map +1 -1
  4. package/dist/ai/asyncQueue.js +2 -0
  5. package/dist/ai/asyncQueue.js.map +1 -1
  6. package/dist/ai/attachmentStaging.d.ts +71 -0
  7. package/dist/ai/attachmentStaging.d.ts.map +1 -0
  8. package/dist/ai/attachmentStaging.js +243 -0
  9. package/dist/ai/attachmentStaging.js.map +1 -0
  10. package/dist/ai/attachmentStore.d.ts +40 -0
  11. package/dist/ai/attachmentStore.d.ts.map +1 -0
  12. package/dist/ai/attachmentStore.js +173 -0
  13. package/dist/ai/attachmentStore.js.map +1 -0
  14. package/dist/ai/cardBuilder.d.ts +3 -2
  15. package/dist/ai/cardBuilder.d.ts.map +1 -1
  16. package/dist/ai/cardBuilder.js +156 -5
  17. package/dist/ai/cardBuilder.js.map +1 -1
  18. package/dist/ai/claudeCliProvider.d.ts +7 -2
  19. package/dist/ai/claudeCliProvider.d.ts.map +1 -1
  20. package/dist/ai/claudeCliProvider.js +29 -11
  21. package/dist/ai/claudeCliProvider.js.map +1 -1
  22. package/dist/ai/claudeCodeProvider.d.ts.map +1 -1
  23. package/dist/ai/claudeCodeProvider.js +2 -0
  24. package/dist/ai/claudeCodeProvider.js.map +1 -1
  25. package/dist/ai/claudeSdkProvider.d.ts +2 -1
  26. package/dist/ai/claudeSdkProvider.d.ts.map +1 -1
  27. package/dist/ai/claudeSdkProvider.js +76 -12
  28. package/dist/ai/claudeSdkProvider.js.map +1 -1
  29. package/dist/ai/codexAppServer/approvalMapping.d.ts.map +1 -1
  30. package/dist/ai/codexAppServer/approvalMapping.js.map +1 -1
  31. package/dist/ai/codexAppServer/cardAdapter.d.ts.map +1 -1
  32. package/dist/ai/codexAppServer/cardAdapter.js.map +1 -1
  33. package/dist/ai/codexAppServer/index.d.ts.map +1 -1
  34. package/dist/ai/codexAppServer/index.js +2 -0
  35. package/dist/ai/codexAppServer/index.js.map +1 -1
  36. package/dist/ai/codexAppServer/overrideStore.d.ts.map +1 -1
  37. package/dist/ai/codexAppServer/overrideStore.js.map +1 -1
  38. package/dist/ai/codexAppServer/permissionMapping.d.ts.map +1 -1
  39. package/dist/ai/codexAppServer/permissionMapping.js.map +1 -1
  40. package/dist/ai/codexAppServer/processManager.d.ts.map +1 -1
  41. package/dist/ai/codexAppServer/processManager.js +2 -0
  42. package/dist/ai/codexAppServer/processManager.js.map +1 -1
  43. package/dist/ai/codexAppServer/provider.d.ts.map +1 -1
  44. package/dist/ai/codexAppServer/provider.js.map +1 -1
  45. package/dist/ai/codexAppServer/rpcClient.d.ts.map +1 -1
  46. package/dist/ai/codexAppServer/rpcClient.js.map +1 -1
  47. package/dist/ai/codexAppServer/schema/index.d.ts.map +1 -1
  48. package/dist/ai/codexAppServer/schema/index.js +2 -0
  49. package/dist/ai/codexAppServer/schema/index.js.map +1 -1
  50. package/dist/ai/codexAppServer/stdioTransport.d.ts.map +1 -1
  51. package/dist/ai/codexAppServer/stdioTransport.js.map +1 -1
  52. package/dist/ai/codexAppServer/tokenAccounting.d.ts.map +1 -1
  53. package/dist/ai/codexAppServer/tokenAccounting.js.map +1 -1
  54. package/dist/ai/codexAppServer/version.d.ts.map +1 -1
  55. package/dist/ai/codexAppServer/version.js +2 -0
  56. package/dist/ai/codexAppServer/version.js.map +1 -1
  57. package/dist/ai/codexLogin.d.ts.map +1 -1
  58. package/dist/ai/codexLogin.js +2 -0
  59. package/dist/ai/codexLogin.js.map +1 -1
  60. package/dist/ai/codexMcpProvider.d.ts.map +1 -1
  61. package/dist/ai/codexMcpProvider.js +2 -0
  62. package/dist/ai/codexMcpProvider.js.map +1 -1
  63. package/dist/ai/commitSummary.d.ts.map +1 -1
  64. package/dist/ai/commitSummary.js +2 -0
  65. package/dist/ai/commitSummary.js.map +1 -1
  66. package/dist/ai/commitSummaryCli.d.ts.map +1 -1
  67. package/dist/ai/commitSummaryCli.js +2 -0
  68. package/dist/ai/commitSummaryCli.js.map +1 -1
  69. package/dist/ai/commitSummaryStore.d.ts.map +1 -1
  70. package/dist/ai/commitSummaryStore.js +2 -0
  71. package/dist/ai/commitSummaryStore.js.map +1 -1
  72. package/dist/ai/contentBlocks.d.ts +9 -0
  73. package/dist/ai/contentBlocks.d.ts.map +1 -0
  74. package/dist/ai/contentBlocks.js +86 -0
  75. package/dist/ai/contentBlocks.js.map +1 -0
  76. package/dist/ai/debugLogger.d.ts.map +1 -1
  77. package/dist/ai/debugLogger.js +2 -0
  78. package/dist/ai/debugLogger.js.map +1 -1
  79. package/dist/ai/enrichEntry.d.ts.map +1 -1
  80. package/dist/ai/enrichEntry.js.map +1 -1
  81. package/dist/ai/pdfMeta.d.ts +8 -0
  82. package/dist/ai/pdfMeta.d.ts.map +1 -0
  83. package/dist/ai/pdfMeta.js +51 -0
  84. package/dist/ai/pdfMeta.js.map +1 -0
  85. package/dist/ai/provider.d.ts +14 -6
  86. package/dist/ai/provider.d.ts.map +1 -1
  87. package/dist/ai/provider.js.map +1 -1
  88. package/dist/ai/sandboxMcp.d.ts.map +1 -1
  89. package/dist/ai/sandboxMcp.js +2 -0
  90. package/dist/ai/sandboxMcp.js.map +1 -1
  91. package/dist/ai/sandboxMcpStdio.d.ts.map +1 -1
  92. package/dist/ai/sandboxMcpStdio.js +2 -0
  93. package/dist/ai/sandboxMcpStdio.js.map +1 -1
  94. package/dist/ai/sessionManager.d.ts +12 -2
  95. package/dist/ai/sessionManager.d.ts.map +1 -1
  96. package/dist/ai/sessionManager.js +39 -5
  97. package/dist/ai/sessionManager.js.map +1 -1
  98. package/dist/ai/sessionRegistry.d.ts.map +1 -1
  99. package/dist/ai/sessionRegistry.js +2 -0
  100. package/dist/ai/sessionRegistry.js.map +1 -1
  101. package/dist/ai/sessionStore.d.ts.map +1 -1
  102. package/dist/ai/sessionStore.js +2 -0
  103. package/dist/ai/sessionStore.js.map +1 -1
  104. package/dist/ai/systemPrompt.d.ts.map +1 -1
  105. package/dist/ai/systemPrompt.js.map +1 -1
  106. package/dist/config.d.ts.map +1 -1
  107. package/dist/config.js +2 -0
  108. package/dist/config.js.map +1 -1
  109. package/dist/connection/connection.d.ts.map +1 -1
  110. package/dist/connection/connection.js +2 -0
  111. package/dist/connection/connection.js.map +1 -1
  112. package/dist/connection/e2eHarness.d.ts.map +1 -1
  113. package/dist/connection/e2eHarness.js +2 -0
  114. package/dist/connection/e2eHarness.js.map +1 -1
  115. package/dist/connection/fakeRelay.d.ts.map +1 -1
  116. package/dist/connection/fakeRelay.js +2 -0
  117. package/dist/connection/fakeRelay.js.map +1 -1
  118. package/dist/connection/pubsub.d.ts.map +1 -1
  119. package/dist/connection/pubsub.js +2 -0
  120. package/dist/connection/pubsub.js.map +1 -1
  121. package/dist/connection/relay.d.ts.map +1 -1
  122. package/dist/connection/relay.js +2 -0
  123. package/dist/connection/relay.js.map +1 -1
  124. package/dist/files/fileBrowser.d.ts.map +1 -1
  125. package/dist/files/fileBrowser.js +2 -0
  126. package/dist/files/fileBrowser.js.map +1 -1
  127. package/dist/git/operations.d.ts.map +1 -1
  128. package/dist/git/operations.js +2 -0
  129. package/dist/git/operations.js.map +1 -1
  130. package/dist/handlers/legacyBusAdapter.d.ts +1 -1
  131. package/dist/handlers/legacyBusAdapter.d.ts.map +1 -1
  132. package/dist/handlers/legacyBusAdapter.js +13 -1
  133. package/dist/handlers/legacyBusAdapter.js.map +1 -1
  134. package/dist/handlers/messageHandler.d.ts +23 -0
  135. package/dist/handlers/messageHandler.d.ts.map +1 -1
  136. package/dist/handlers/messageHandler.js +223 -7
  137. package/dist/handlers/messageHandler.js.map +1 -1
  138. package/dist/index.js +42 -2
  139. package/dist/index.js.map +1 -1
  140. package/dist/messageBus/busServerTransport.d.ts.map +1 -1
  141. package/dist/messageBus/busServerTransport.js +2 -0
  142. package/dist/messageBus/busServerTransport.js.map +1 -1
  143. package/dist/service/debugHttpServer.d.ts.map +1 -1
  144. package/dist/service/debugHttpServer.js +2 -0
  145. package/dist/service/debugHttpServer.js.map +1 -1
  146. package/dist/service/ensureDaemon.d.ts.map +1 -1
  147. package/dist/service/ensureDaemon.js +27 -2
  148. package/dist/service/ensureDaemon.js.map +1 -1
  149. package/dist/service/index.d.ts.map +1 -1
  150. package/dist/service/index.js +2 -0
  151. package/dist/service/index.js.map +1 -1
  152. package/dist/service/ipcClient.d.ts.map +1 -1
  153. package/dist/service/ipcClient.js +3 -1
  154. package/dist/service/ipcClient.js.map +1 -1
  155. package/dist/service/ipcServer.d.ts.map +1 -1
  156. package/dist/service/ipcServer.js +2 -0
  157. package/dist/service/ipcServer.js.map +1 -1
  158. package/dist/service/pushClient.d.ts.map +1 -1
  159. package/dist/service/pushClient.js +2 -0
  160. package/dist/service/pushClient.js.map +1 -1
  161. package/dist/service/run.d.ts.map +1 -1
  162. package/dist/service/run.js +14 -1
  163. package/dist/service/run.js.map +1 -1
  164. package/dist/service/singleton.d.ts +1 -0
  165. package/dist/service/singleton.d.ts.map +1 -1
  166. package/dist/service/singleton.js +5 -0
  167. package/dist/service/singleton.js.map +1 -1
  168. package/dist/service/stateStore.d.ts.map +1 -1
  169. package/dist/service/stateStore.js +2 -0
  170. package/dist/service/stateStore.js.map +1 -1
  171. package/dist/service/systemdUnit.d.ts +149 -0
  172. package/dist/service/systemdUnit.d.ts.map +1 -0
  173. package/dist/service/systemdUnit.js +322 -0
  174. package/dist/service/systemdUnit.js.map +1 -0
  175. package/dist/service/types.d.ts +6 -0
  176. package/dist/service/types.d.ts.map +1 -1
  177. package/dist/service/types.js +3 -1
  178. package/dist/service/types.js.map +1 -1
  179. package/dist/storage/eventStore.d.ts.map +1 -1
  180. package/dist/storage/eventStore.js +2 -0
  181. package/dist/storage/eventStore.js.map +1 -1
  182. package/dist/terminal/terminalManager.d.ts.map +1 -1
  183. package/dist/terminal/terminalManager.js +2 -0
  184. package/dist/terminal/terminalManager.js.map +1 -1
  185. package/dist/tombstoneCheck.d.ts.map +1 -1
  186. package/dist/tombstoneCheck.js +2 -0
  187. package/dist/tombstoneCheck.js.map +1 -1
  188. package/package.json +3 -3
@@ -1,3 +1,5 @@
1
+ // SPDX-FileCopyrightText: 2026 King Young Technology
2
+ // SPDX-License-Identifier: MIT
1
3
  import { createMessage, } from '@sumicom/quicksave-shared';
2
4
  /**
3
5
  * Verbs from the legacy `MessageHandler` switch that are exposed as bus
@@ -11,7 +13,7 @@ import { createMessage, } from '@sumicom/quicksave-shared';
11
13
  * touching:
12
14
  * 1. `messageHandler.ts` switch (the handler itself)
13
15
  * 2. This array (the bus exposure)
14
- * 3. The relevant docs (`docs/references/quicksave-architecture.md`)
16
+ * 3. The relevant docs (`docs/references/quicksave-architecture.en.md`)
15
17
  */
16
18
  export const LEGACY_BUS_VERBS = [
17
19
  'ping',
@@ -52,6 +54,10 @@ export const LEGACY_BUS_VERBS = [
52
54
  'agent:check-update',
53
55
  'agent:update',
54
56
  'agent:restart',
57
+ // systemd user-unit (Linux only — agent returns available:false elsewhere)
58
+ 'systemd:status',
59
+ 'systemd:install',
60
+ 'systemd:uninstall',
55
61
  // codex
56
62
  'codex:list-models',
57
63
  'codex:login-start',
@@ -73,6 +79,7 @@ export const LEGACY_BUS_VERBS = [
73
79
  'session:update-history',
74
80
  'session:delete-history',
75
81
  'session:list-archived',
82
+ 'session:mark-read',
76
83
  // project
77
84
  'project:list-summaries',
78
85
  'project:list-repos',
@@ -88,6 +95,10 @@ export const LEGACY_BUS_VERBS = [
88
95
  // files
89
96
  'files:list',
90
97
  'files:read',
98
+ // attachments
99
+ 'attachment:upload',
100
+ 'attachment:cancel',
101
+ 'attachment:fetch',
91
102
  ];
92
103
  /**
93
104
  * Bridge legacy `MessageHandler` request/response verbs onto a
@@ -105,6 +116,7 @@ export const LEGACY_BUS_VERBS = [
105
116
  * callers can `String#startsWith` to detect specific codes.
106
117
  */
107
118
  export function wireLegacyBusVerbs(bus, handler, verbs = LEGACY_BUS_VERBS) {
119
+ console.log(`[legacy-bus] wiring ${verbs.length} verbs; attachment* present: ${verbs.filter(v => v.startsWith('attachment:')).join(',') || 'NONE'}`);
108
120
  for (const verb of verbs) {
109
121
  bus.onCommand(verb, async (rawPayload, { peer }) => {
110
122
  let repoPath;
@@ -1 +1 @@
1
- {"version":3,"file":"legacyBusAdapter.js","sourceRoot":"","sources":["../../src/handlers/legacyBusAdapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"legacyBusAdapter.js","sourceRoot":"","sources":["../../src/handlers/legacyBusAdapter.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,7 @@ import { Message, License, Repository, SessionRegistryEntry, AgentId, CodexModel
2
2
  import type { PushClient } from '../service/pushClient.js';
3
3
  import { CommitSummaryStateStore } from '../ai/commitSummaryStore.js';
4
4
  import { SessionManager } from '../ai/sessionManager.js';
5
+ import { AttachmentStaging } from '../ai/attachmentStaging.js';
5
6
  import { CodexLoginManager } from '../ai/codexLogin.js';
6
7
  export declare class MessageHandler {
7
8
  private repos;
@@ -17,6 +18,8 @@ export declare class MessageHandler {
17
18
  * `state-updated` event to `connection.broadcast` (see service/run.ts). */
18
19
  private commitSummaryStore;
19
20
  private claudeService;
21
+ private attachmentStaging;
22
+ private attachmentGcTimer;
20
23
  private pushClient;
21
24
  private latestVersionCache;
22
25
  private versionCheckInFlight;
@@ -172,6 +175,9 @@ export declare class MessageHandler {
172
175
  private handleAgentCheckUpdate;
173
176
  private handleAgentUpdate;
174
177
  private handleAgentRestart;
178
+ private handleSystemdStatus;
179
+ private handleSystemdInstall;
180
+ private handleSystemdUninstall;
175
181
  private handleCodexListModels;
176
182
  /**
177
183
  * Kick off the Codex device-auth OAuth flow on the daemon. Returns the
@@ -208,6 +214,18 @@ export declare class MessageHandler {
208
214
  private handleUpdateHistory;
209
215
  private handleDeleteHistory;
210
216
  private handleListArchived;
217
+ /**
218
+ * Stamp `lastReadAt` on a registry entry and broadcast the update so every
219
+ * connected PWA client converges on the same read state. The "unread" mark
220
+ * lives server-side under the user's group identity (see
221
+ * `docs/guidelines/sync-security.en.md`) — there's no per-device scoping,
222
+ * so reading on the phone clears the badge on the desktop too.
223
+ *
224
+ * Idempotent: late-arriving / out-of-order viewedAt values that are older
225
+ * than the persisted timestamp are dropped (max-wins) so a slow tab can't
226
+ * undo a fresh read from a faster one.
227
+ */
228
+ private handleMarkRead;
211
229
  private handleListProjectSummaries;
212
230
  private handleListProjectRepos;
213
231
  /**
@@ -236,6 +254,11 @@ export declare class MessageHandler {
236
254
  private handleTerminalRename;
237
255
  private handleFilesList;
238
256
  private handleFilesRead;
257
+ /** Exposed so other agent paths (sessionManager) can resolve staged ids. */
258
+ getAttachmentStaging(): AttachmentStaging;
259
+ private handleAttachmentUpload;
260
+ private handleAttachmentCancel;
261
+ private handleAttachmentFetch;
239
262
  private createErrorResponse;
240
263
  }
241
264
  //# sourceMappingURL=messageHandler.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"messageHandler.d.ts","sourceRoot":"","sources":["../../src/handlers/messageHandler.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"messageHandler.d.ts","sourceRoot":"","sources":["../../src/handlers/messageHandler.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,7 @@
1
+ // SPDX-FileCopyrightText: 2026 King Young Technology
2
+ // SPDX-License-Identifier: MIT
1
3
  import { createMessage, } from '@sumicom/quicksave-shared';
4
+ import { getSystemdStatus, installUserUnit, uninstallUserUnit, systemctlAvailable, } from '../service/systemdUnit.js';
2
5
  import { GitOperations } from '../git/operations.js';
3
6
  import { getAnthropicApiKey, setAnthropicApiKey, hasAnthropicApiKey, addManagedRepo, removeManagedRepo, addManagedCodingPath, removeManagedCodingPath } from '../config.js';
4
7
  import { CommitSummaryService } from '../ai/commitSummary.js';
@@ -6,6 +9,8 @@ import { CommitSummaryCliService, CommitSummaryCliError } from '../ai/commitSumm
6
9
  import { CommitSummaryStateStore } from '../ai/commitSummaryStore.js';
7
10
  import { SessionManager } from '../ai/sessionManager.js';
8
11
  import { ClaudeCodeProvider } from '../ai/claudeCodeProvider.js';
12
+ import { AttachmentStaging } from '../ai/attachmentStaging.js';
13
+ import { loadAttachment, removeSessionAttachments } from '../ai/attachmentStore.js';
9
14
  import { CodexAppServerProvider } from '../ai/codexAppServer/index.js';
10
15
  import { CodexLoginManager } from '../ai/codexLogin.js';
11
16
  import { getTerminalManager } from '../terminal/terminalManager.js';
@@ -16,7 +21,7 @@ import { getEventStore } from '../storage/eventStore.js';
16
21
  import { readdir, stat, readFile } from 'fs/promises';
17
22
  import { existsSync } from 'fs';
18
23
  import { join, dirname, basename } from 'path';
19
- import { homedir } from 'os';
24
+ import { homedir, platform as osPlatform } from 'os';
20
25
  import { spawnAppServer } from '../ai/codexAppServer/index.js';
21
26
  const VERSION_CHECK_INTERVAL_MS = 12 * 60 * 60 * 1000; // 12 hours
22
27
  // model/list reflects the current authenticated account's available models.
@@ -24,6 +29,14 @@ const VERSION_CHECK_INTERVAL_MS = 12 * 60 * 60 * 1000; // 12 hours
24
29
  // every PWA reload; short enough that plan upgrades / model rollouts surface
25
30
  // within the same session day. Force-refreshes still bypass the TTL.
26
31
  const CODEX_MODELS_TTL_MS = 30 * 60 * 1000;
32
+ /** Coerce `os.platform()` into the narrow union the PWA expects. Anything
33
+ * outside the three first-class platforms gets `'other'` so the field is
34
+ * always present and the PWA doesn't have to handle `undefined`. */
35
+ function normalizePlatform(p) {
36
+ if (p === 'linux' || p === 'darwin' || p === 'win32')
37
+ return p;
38
+ return 'other';
39
+ }
27
40
  /** Shallow equality on a model list. Used to suppress no-op
28
41
  * `/codex/models` broadcasts after a TTL refresh that returned the
29
42
  * same list. */
@@ -74,7 +87,7 @@ function projectAppServerModel(m) {
74
87
  }
75
88
  export class MessageHandler {
76
89
  repos;
77
- agentVersion = '0.8.6';
90
+ agentVersion = '0.8.7';
78
91
  defaultRepoPath;
79
92
  clientRepos = new Map(); // peerAddress -> repoPath
80
93
  repoLocks = new Map(); // repoPath -> peerAddress holding lock
@@ -86,6 +99,8 @@ export class MessageHandler {
86
99
  * `state-updated` event to `connection.broadcast` (see service/run.ts). */
87
100
  commitSummaryStore = new CommitSummaryStateStore();
88
101
  claudeService;
102
+ attachmentStaging = new AttachmentStaging();
103
+ attachmentGcTimer = null;
89
104
  pushClient = null;
90
105
  latestVersionCache = null;
91
106
  versionCheckInFlight = null;
@@ -117,6 +132,10 @@ export class MessageHandler {
117
132
  this.codingPaths.set(p, { path: p, name: basename(p) });
118
133
  }
119
134
  }
135
+ this.attachmentGcTimer = setInterval(() => this.attachmentStaging.gc(), 60_000);
136
+ if (typeof this.attachmentGcTimer.unref === 'function') {
137
+ this.attachmentGcTimer.unref();
138
+ }
120
139
  }
121
140
  /**
122
141
  * Check npm registry for latest version. Deduped: returns cached value
@@ -359,10 +378,17 @@ export class MessageHandler {
359
378
  this.repoLocks.delete(repoPath);
360
379
  }
361
380
  }
381
+ // Drop any in-flight uploads from this peer; on reconnect the PWA will
382
+ // re-stage them via the upload manager's resume path.
383
+ this.attachmentStaging.removePeer(peerAddress);
362
384
  // Pending user input requests are NOT auto-approved on disconnect.
363
385
  // They persist until the user reconnects and explicitly responds.
364
386
  }
365
387
  cleanup() {
388
+ if (this.attachmentGcTimer) {
389
+ clearInterval(this.attachmentGcTimer);
390
+ this.attachmentGcTimer = null;
391
+ }
366
392
  this.claudeService.cleanup();
367
393
  }
368
394
  getActiveSessionCount() {
@@ -397,7 +423,8 @@ export class MessageHandler {
397
423
  async handleMessage(message, peerAddress = 'default') {
398
424
  const isVerbose = message.type.startsWith('claude:') ||
399
425
  message.type.startsWith('agent:add') ||
400
- message.type.startsWith('project:');
426
+ message.type.startsWith('project:') ||
427
+ message.type.startsWith('attachment:');
401
428
  if (isVerbose) {
402
429
  console.log(`[msg] ${message.type} from ${peerAddress.slice(0, 12)}`);
403
430
  }
@@ -498,6 +525,12 @@ export class MessageHandler {
498
525
  return this.handleAgentUpdate(message);
499
526
  case 'agent:restart':
500
527
  return this.handleAgentRestart(message);
528
+ case 'systemd:status':
529
+ return this.handleSystemdStatus(message);
530
+ case 'systemd:install':
531
+ return this.handleSystemdInstall(message);
532
+ case 'systemd:uninstall':
533
+ return this.handleSystemdUninstall(message);
501
534
  case 'codex:list-models':
502
535
  return this.handleCodexListModels(message);
503
536
  case 'codex:login-start':
@@ -535,6 +568,8 @@ export class MessageHandler {
535
568
  return this.handleDeleteHistory(message);
536
569
  case 'session:list-archived':
537
570
  return this.handleListArchived(message);
571
+ case 'session:mark-read':
572
+ return this.handleMarkRead(message);
538
573
  case 'project:list-summaries':
539
574
  return this.handleListProjectSummaries(message);
540
575
  case 'project:list-repos':
@@ -557,6 +592,12 @@ export class MessageHandler {
557
592
  return this.handleFilesList(message);
558
593
  case 'files:read':
559
594
  return this.handleFilesRead(message);
595
+ case 'attachment:upload':
596
+ return this.handleAttachmentUpload(message, peerAddress);
597
+ case 'attachment:cancel':
598
+ return this.handleAttachmentCancel(message, peerAddress);
599
+ case 'attachment:fetch':
600
+ return this.handleAttachmentFetch(message);
560
601
  default:
561
602
  return this.createErrorResponse(message.id, 'UNKNOWN_MESSAGE_TYPE', `Unknown message type: ${message.type}`);
562
603
  }
@@ -575,6 +616,7 @@ export class MessageHandler {
575
616
  latestVersion: this.latestVersionCache?.version,
576
617
  devBuild: !this.productionBuild || undefined,
577
618
  codexModels: this.codexModelsCache?.models,
619
+ platform: normalizePlatform(osPlatform()),
578
620
  });
579
621
  response.id = message.id;
580
622
  return response;
@@ -1506,6 +1548,77 @@ export class MessageHandler {
1506
1548
  return response;
1507
1549
  }
1508
1550
  }
1551
+ // ==========================================================================
1552
+ // systemd user-unit (Linux auto-start at login)
1553
+ //
1554
+ // The PWA toggles install/uninstall from the per-machine settings page.
1555
+ // These handlers are no-ops on non-Linux hosts (the status payload's
1556
+ // `available: false` field is the gate the PWA uses to hide the section,
1557
+ // so non-Linux still gets a well-formed response instead of an error).
1558
+ // ==========================================================================
1559
+ handleSystemdStatus(message) {
1560
+ const status = getSystemdStatus();
1561
+ const response = createMessage('systemd:status:response', status);
1562
+ response.id = message.id;
1563
+ return response;
1564
+ }
1565
+ handleSystemdInstall(message) {
1566
+ if (!systemctlAvailable()) {
1567
+ const response = createMessage('systemd:install:response', {
1568
+ success: false,
1569
+ error: 'systemctl --user is not available on this host',
1570
+ status: getSystemdStatus(),
1571
+ });
1572
+ response.id = message.id;
1573
+ return response;
1574
+ }
1575
+ const result = installUserUnit();
1576
+ const response = createMessage('systemd:install:response', result);
1577
+ response.id = message.id;
1578
+ return response;
1579
+ }
1580
+ handleSystemdUninstall(message) {
1581
+ // Uninstalling can stop the systemd-managed daemon as a side effect — if
1582
+ // the running process IS that daemon, `disable --now` would kill our IPC
1583
+ // server before the response reaches the PWA. So we compute the response
1584
+ // up front (which captures the pre-uninstall status) but defer the
1585
+ // actual mutation until next tick, giving the bus transport a chance to
1586
+ // flush. The next CLI invocation will hit `ensureDaemon`, which falls
1587
+ // back to detached spawn now that the unit is gone.
1588
+ if (!systemctlAvailable()) {
1589
+ const response = createMessage('systemd:uninstall:response', {
1590
+ success: false,
1591
+ error: 'systemctl --user is not available on this host',
1592
+ status: getSystemdStatus(),
1593
+ });
1594
+ response.id = message.id;
1595
+ return response;
1596
+ }
1597
+ setImmediate(() => {
1598
+ try {
1599
+ uninstallUserUnit();
1600
+ }
1601
+ catch (err) {
1602
+ console.error('[systemd] deferred uninstall failed:', err);
1603
+ }
1604
+ });
1605
+ // Optimistic response: report the post-uninstall shape (unitInstalled,
1606
+ // unitEnabled, isActive all expected to flip false). We can't actually
1607
+ // observe the post state because we haven't run the disable yet.
1608
+ const optimistic = getSystemdStatus();
1609
+ const response = createMessage('systemd:uninstall:response', {
1610
+ success: true,
1611
+ status: {
1612
+ ...optimistic,
1613
+ unitInstalled: false,
1614
+ unitEnabled: false,
1615
+ isActive: false,
1616
+ currentExecStart: undefined,
1617
+ },
1618
+ });
1619
+ response.id = message.id;
1620
+ return response;
1621
+ }
1509
1622
  async handleCodexListModels(message) {
1510
1623
  try {
1511
1624
  const [models, auth] = await Promise.all([
@@ -1558,7 +1671,7 @@ export class MessageHandler {
1558
1671
  // Claude Code SDK Handlers
1559
1672
  // ============================================================================
1560
1673
  async handleClaudeStart(message, peerAddress) {
1561
- const { prompt, cwd: payloadCwd, agent, allowedTools, systemPrompt, model, permissionMode, sandboxed, reasoningEffort, contextWindow } = message.payload;
1674
+ const { prompt, cwd: payloadCwd, agent, allowedTools, systemPrompt, model, permissionMode, sandboxed, reasoningEffort, contextWindow, attachmentIds } = message.payload;
1562
1675
  const legacyProvider = message.payload.provider;
1563
1676
  const cwd = payloadCwd || this.getClientRepoPath(peerAddress);
1564
1677
  const resolvedAgent = agent ?? (legacyProvider === 'codex-mcp' ? 'codex' : legacyProvider ? 'claude-code' : undefined);
@@ -1568,7 +1681,20 @@ export class MessageHandler {
1568
1681
  // turn/start with `BadRequest`. Pass-through for non-codex agents and
1569
1682
  // when the cache hasn't loaded yet.
1570
1683
  const validatedModel = this.validateCodexModel(model, resolvedAgent ?? 'claude-code');
1571
- console.log(`[agent:start] agent=${resolvedAgent ?? 'default'} model=${validatedModel ?? 'default'}${validatedModel !== model ? ` (coerced from ${model})` : ''} cwd=${cwd} prompt=${prompt.slice(0, 80)}${sandboxed ? ' [sandboxed]' : ''}`);
1684
+ console.log(`[agent:start] agent=${resolvedAgent ?? 'default'} model=${validatedModel ?? 'default'}${validatedModel !== model ? ` (coerced from ${model})` : ''} cwd=${cwd} prompt=${prompt.slice(0, 80)}${sandboxed ? ' [sandboxed]' : ''}${attachmentIds && attachmentIds.length > 0 ? ` [+${attachmentIds.length} attachments]` : ''}`);
1685
+ let attachments;
1686
+ if (attachmentIds && attachmentIds.length > 0) {
1687
+ try {
1688
+ attachments = this.attachmentStaging.consume(peerAddress, attachmentIds);
1689
+ }
1690
+ catch (error) {
1691
+ const code = error.code ?? 'attachment_error';
1692
+ const text = error instanceof Error ? error.message : 'attachment resolve failed';
1693
+ const response = createMessage('claude:start:response', { success: false, error: `${code}: ${text}` });
1694
+ response.id = message.id;
1695
+ return response;
1696
+ }
1697
+ }
1572
1698
  try {
1573
1699
  const sessionId = await this.claudeService.startSession({
1574
1700
  prompt,
@@ -1581,6 +1707,7 @@ export class MessageHandler {
1581
1707
  sandboxed,
1582
1708
  reasoningEffort,
1583
1709
  contextWindow,
1710
+ attachments,
1584
1711
  });
1585
1712
  console.log(`[agent:start] session created: ${sessionId} agent=${resolvedAgent ?? 'default'}`);
1586
1713
  // Register in session history
@@ -1622,18 +1749,32 @@ export class MessageHandler {
1622
1749
  }
1623
1750
  }
1624
1751
  async handleClaudeResume(message, peerAddress) {
1625
- const { sessionId: requestedId, prompt, cwd: payloadCwd, agent } = message.payload;
1752
+ const { sessionId: requestedId, prompt, cwd: payloadCwd, agent, attachmentIds } = message.payload;
1626
1753
  const legacyProvider = message.payload.provider;
1627
1754
  const cwd = payloadCwd || this.getClientRepoPath(peerAddress);
1628
1755
  const resolvedAgent = agent ?? (legacyProvider === 'codex-mcp' ? 'codex' : legacyProvider ? 'claude-code' : undefined);
1629
1756
  const activeCfg = this.claudeService.getSessionConfig(requestedId);
1630
- console.log(`[agent:resume] session=${requestedId} agent=${resolvedAgent ?? 'stored'} model=${activeCfg.model ?? 'default'} cwd=${cwd} prompt=${prompt.slice(0, 80)}`);
1757
+ console.log(`[agent:resume] session=${requestedId} agent=${resolvedAgent ?? 'stored'} model=${activeCfg.model ?? 'default'} cwd=${cwd} prompt=${prompt.slice(0, 80)}${attachmentIds && attachmentIds.length > 0 ? ` [+${attachmentIds.length} attachments]` : ''}`);
1758
+ let attachments;
1759
+ if (attachmentIds && attachmentIds.length > 0) {
1760
+ try {
1761
+ attachments = this.attachmentStaging.consume(peerAddress, attachmentIds);
1762
+ }
1763
+ catch (error) {
1764
+ const code = error.code ?? 'attachment_error';
1765
+ const text = error instanceof Error ? error.message : 'attachment resolve failed';
1766
+ const response = createMessage('claude:resume:response', { success: false, error: `${code}: ${text}` });
1767
+ response.id = message.id;
1768
+ return response;
1769
+ }
1770
+ }
1631
1771
  try {
1632
1772
  const actualSessionId = await this.claudeService.resumeSession({
1633
1773
  sessionId: requestedId,
1634
1774
  prompt,
1635
1775
  cwd,
1636
1776
  agent: resolvedAgent,
1777
+ attachments,
1637
1778
  });
1638
1779
  console.log(`[agent:resume] session resumed: ${actualSessionId}`);
1639
1780
  getEventStore().record({
@@ -1708,6 +1849,11 @@ export class MessageHandler {
1708
1849
  }
1709
1850
  }
1710
1851
  const closed = this.claudeService.closeSession(sessionId);
1852
+ // Drop persisted attachment bytes for this session — fire-and-forget;
1853
+ // a stuck rm shouldn't block the response.
1854
+ removeSessionAttachments(sessionId).catch((err) => {
1855
+ console.error(`[agent:end-task] removeSessionAttachments failed session=${sessionId}:`, err);
1856
+ });
1711
1857
  const success = closed || archived;
1712
1858
  console.log(`[agent:end-task] session=${sessionId} closed=${closed} archived=${archived} cwd=${cwd ?? 'unknown'}`);
1713
1859
  const response = createMessage('claude:end-task:response', { success, error: success ? undefined : 'Session not found' });
@@ -1842,6 +1988,42 @@ export class MessageHandler {
1842
1988
  response.id = message.id;
1843
1989
  return response;
1844
1990
  }
1991
+ /**
1992
+ * Stamp `lastReadAt` on a registry entry and broadcast the update so every
1993
+ * connected PWA client converges on the same read state. The "unread" mark
1994
+ * lives server-side under the user's group identity (see
1995
+ * `docs/guidelines/sync-security.en.md`) — there's no per-device scoping,
1996
+ * so reading on the phone clears the badge on the desktop too.
1997
+ *
1998
+ * Idempotent: late-arriving / out-of-order viewedAt values that are older
1999
+ * than the persisted timestamp are dropped (max-wins) so a slow tab can't
2000
+ * undo a fresh read from a faster one.
2001
+ */
2002
+ handleMarkRead(message) {
2003
+ const { sessionId, cwd, viewedAt } = message.payload;
2004
+ const stamp = typeof viewedAt === 'number' ? viewedAt : Date.now();
2005
+ const existing = getSessionRegistry().getEntry(cwd, sessionId);
2006
+ if (!existing) {
2007
+ const failed = createMessage('session:mark-read:response', { success: false, error: 'Entry not found' });
2008
+ failed.id = message.id;
2009
+ return failed;
2010
+ }
2011
+ const nextLastReadAt = Math.max(existing.lastReadAt ?? 0, stamp);
2012
+ let entry = existing;
2013
+ if (nextLastReadAt !== existing.lastReadAt) {
2014
+ const updated = getSessionRegistry().updateEntry(cwd, sessionId, { lastReadAt: nextLastReadAt });
2015
+ if (updated)
2016
+ entry = updated;
2017
+ // Broadcast on /sessions/history so other PWA clients see the new
2018
+ // read state immediately. /sessions/active is also re-emitted via
2019
+ // sessionManager so the live-stats path stays consistent.
2020
+ this.onHistoryUpdated?.(cwd, entry, 'upsert');
2021
+ this.claudeService.emitSessionUpdate(sessionId);
2022
+ }
2023
+ const response = createMessage('session:mark-read:response', { success: true, lastReadAt: entry.lastReadAt });
2024
+ response.id = message.id;
2025
+ return response;
2026
+ }
1845
2027
  handleListProjectSummaries(message) {
1846
2028
  const allEntries = getSessionRegistry().getEntriesForProject();
1847
2029
  // Group by cwd
@@ -2162,6 +2344,40 @@ export class MessageHandler {
2162
2344
  response.id = message.id;
2163
2345
  return response;
2164
2346
  }
2347
+ // ── Attachment staging handlers ──────────────────────────────────────────
2348
+ /** Exposed so other agent paths (sessionManager) can resolve staged ids. */
2349
+ getAttachmentStaging() {
2350
+ return this.attachmentStaging;
2351
+ }
2352
+ handleAttachmentUpload(message, peerAddress) {
2353
+ try {
2354
+ const result = this.attachmentStaging.acceptChunk(peerAddress, message.payload);
2355
+ const response = createMessage('attachment:upload:response', result);
2356
+ response.id = message.id;
2357
+ return response;
2358
+ }
2359
+ catch (error) {
2360
+ const code = error.code ?? 'attachment_error';
2361
+ const text = error instanceof Error ? error.message : 'attachment upload failed';
2362
+ return this.createErrorResponse(message.id, code, text);
2363
+ }
2364
+ }
2365
+ handleAttachmentCancel(message, peerAddress) {
2366
+ const removed = this.attachmentStaging.cancel(peerAddress, message.payload.attachmentId);
2367
+ const response = createMessage('attachment:cancel:response', { removed });
2368
+ response.id = message.id;
2369
+ return response;
2370
+ }
2371
+ async handleAttachmentFetch(message) {
2372
+ const { sessionId, attachmentId } = message.payload;
2373
+ const attachment = await loadAttachment(sessionId, attachmentId);
2374
+ if (!attachment) {
2375
+ return this.createErrorResponse(message.id, 'attachment_not_found', `attachment ${attachmentId} not on disk for session ${sessionId}`);
2376
+ }
2377
+ const response = createMessage('attachment:fetch:response', { attachment });
2378
+ response.id = message.id;
2379
+ return response;
2380
+ }
2165
2381
  createErrorResponse(id, code, message) {
2166
2382
  const response = createMessage('error', { code, message });
2167
2383
  response.id = id;