@sumicom/quicksave 0.8.5 → 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 (193) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +31 -93
  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 +6 -6
  15. package/dist/ai/cardBuilder.d.ts.map +1 -1
  16. package/dist/ai/cardBuilder.js +164 -16
  17. package/dist/ai/cardBuilder.js.map +1 -1
  18. package/dist/ai/claudeCliProvider.d.ts +34 -15
  19. package/dist/ai/claudeCliProvider.d.ts.map +1 -1
  20. package/dist/ai/claudeCliProvider.js +108 -64
  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 +6 -3
  26. package/dist/ai/claudeSdkProvider.d.ts.map +1 -1
  27. package/dist/ai/claudeSdkProvider.js +97 -29
  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 +0 -3
  32. package/dist/ai/codexAppServer/cardAdapter.d.ts.map +1 -1
  33. package/dist/ai/codexAppServer/cardAdapter.js +40 -5
  34. package/dist/ai/codexAppServer/cardAdapter.js.map +1 -1
  35. package/dist/ai/codexAppServer/index.d.ts.map +1 -1
  36. package/dist/ai/codexAppServer/index.js +2 -0
  37. package/dist/ai/codexAppServer/index.js.map +1 -1
  38. package/dist/ai/codexAppServer/overrideStore.d.ts.map +1 -1
  39. package/dist/ai/codexAppServer/overrideStore.js.map +1 -1
  40. package/dist/ai/codexAppServer/permissionMapping.d.ts.map +1 -1
  41. package/dist/ai/codexAppServer/permissionMapping.js.map +1 -1
  42. package/dist/ai/codexAppServer/processManager.d.ts.map +1 -1
  43. package/dist/ai/codexAppServer/processManager.js +2 -0
  44. package/dist/ai/codexAppServer/processManager.js.map +1 -1
  45. package/dist/ai/codexAppServer/provider.d.ts.map +1 -1
  46. package/dist/ai/codexAppServer/provider.js +9 -24
  47. package/dist/ai/codexAppServer/provider.js.map +1 -1
  48. package/dist/ai/codexAppServer/rpcClient.d.ts.map +1 -1
  49. package/dist/ai/codexAppServer/rpcClient.js.map +1 -1
  50. package/dist/ai/codexAppServer/schema/index.d.ts.map +1 -1
  51. package/dist/ai/codexAppServer/schema/index.js +2 -0
  52. package/dist/ai/codexAppServer/schema/index.js.map +1 -1
  53. package/dist/ai/codexAppServer/stdioTransport.d.ts.map +1 -1
  54. package/dist/ai/codexAppServer/stdioTransport.js.map +1 -1
  55. package/dist/ai/codexAppServer/tokenAccounting.d.ts.map +1 -1
  56. package/dist/ai/codexAppServer/tokenAccounting.js.map +1 -1
  57. package/dist/ai/codexAppServer/version.d.ts.map +1 -1
  58. package/dist/ai/codexAppServer/version.js +2 -0
  59. package/dist/ai/codexAppServer/version.js.map +1 -1
  60. package/dist/ai/codexLogin.d.ts.map +1 -1
  61. package/dist/ai/codexLogin.js +2 -0
  62. package/dist/ai/codexLogin.js.map +1 -1
  63. package/dist/ai/codexMcpProvider.d.ts.map +1 -1
  64. package/dist/ai/codexMcpProvider.js +7 -10
  65. package/dist/ai/codexMcpProvider.js.map +1 -1
  66. package/dist/ai/commitSummary.d.ts.map +1 -1
  67. package/dist/ai/commitSummary.js +2 -0
  68. package/dist/ai/commitSummary.js.map +1 -1
  69. package/dist/ai/commitSummaryCli.d.ts.map +1 -1
  70. package/dist/ai/commitSummaryCli.js +2 -0
  71. package/dist/ai/commitSummaryCli.js.map +1 -1
  72. package/dist/ai/commitSummaryStore.d.ts.map +1 -1
  73. package/dist/ai/commitSummaryStore.js +2 -0
  74. package/dist/ai/commitSummaryStore.js.map +1 -1
  75. package/dist/ai/contentBlocks.d.ts +9 -0
  76. package/dist/ai/contentBlocks.d.ts.map +1 -0
  77. package/dist/ai/contentBlocks.js +86 -0
  78. package/dist/ai/contentBlocks.js.map +1 -0
  79. package/dist/ai/debugLogger.d.ts.map +1 -1
  80. package/dist/ai/debugLogger.js +2 -0
  81. package/dist/ai/debugLogger.js.map +1 -1
  82. package/dist/ai/enrichEntry.d.ts.map +1 -1
  83. package/dist/ai/enrichEntry.js.map +1 -1
  84. package/dist/ai/pdfMeta.d.ts +8 -0
  85. package/dist/ai/pdfMeta.d.ts.map +1 -0
  86. package/dist/ai/pdfMeta.js +51 -0
  87. package/dist/ai/pdfMeta.js.map +1 -0
  88. package/dist/ai/provider.d.ts +21 -8
  89. package/dist/ai/provider.d.ts.map +1 -1
  90. package/dist/ai/provider.js.map +1 -1
  91. package/dist/ai/sandboxMcp.d.ts.map +1 -1
  92. package/dist/ai/sandboxMcp.js +2 -0
  93. package/dist/ai/sandboxMcp.js.map +1 -1
  94. package/dist/ai/sandboxMcpStdio.d.ts.map +1 -1
  95. package/dist/ai/sandboxMcpStdio.js +2 -0
  96. package/dist/ai/sandboxMcpStdio.js.map +1 -1
  97. package/dist/ai/sessionManager.d.ts +19 -7
  98. package/dist/ai/sessionManager.d.ts.map +1 -1
  99. package/dist/ai/sessionManager.js +81 -23
  100. package/dist/ai/sessionManager.js.map +1 -1
  101. package/dist/ai/sessionRegistry.d.ts.map +1 -1
  102. package/dist/ai/sessionRegistry.js +2 -0
  103. package/dist/ai/sessionRegistry.js.map +1 -1
  104. package/dist/ai/sessionStore.d.ts.map +1 -1
  105. package/dist/ai/sessionStore.js +2 -0
  106. package/dist/ai/sessionStore.js.map +1 -1
  107. package/dist/ai/systemPrompt.d.ts.map +1 -1
  108. package/dist/ai/systemPrompt.js +3 -0
  109. package/dist/ai/systemPrompt.js.map +1 -1
  110. package/dist/config.d.ts.map +1 -1
  111. package/dist/config.js +2 -0
  112. package/dist/config.js.map +1 -1
  113. package/dist/connection/connection.d.ts.map +1 -1
  114. package/dist/connection/connection.js +2 -0
  115. package/dist/connection/connection.js.map +1 -1
  116. package/dist/connection/e2eHarness.d.ts +1 -1
  117. package/dist/connection/e2eHarness.d.ts.map +1 -1
  118. package/dist/connection/e2eHarness.js +26 -8
  119. package/dist/connection/e2eHarness.js.map +1 -1
  120. package/dist/connection/fakeRelay.d.ts.map +1 -1
  121. package/dist/connection/fakeRelay.js +2 -0
  122. package/dist/connection/fakeRelay.js.map +1 -1
  123. package/dist/connection/pubsub.d.ts.map +1 -1
  124. package/dist/connection/pubsub.js +2 -0
  125. package/dist/connection/pubsub.js.map +1 -1
  126. package/dist/connection/relay.d.ts.map +1 -1
  127. package/dist/connection/relay.js +2 -0
  128. package/dist/connection/relay.js.map +1 -1
  129. package/dist/files/fileBrowser.d.ts.map +1 -1
  130. package/dist/files/fileBrowser.js +44 -2
  131. package/dist/files/fileBrowser.js.map +1 -1
  132. package/dist/git/operations.d.ts.map +1 -1
  133. package/dist/git/operations.js +2 -0
  134. package/dist/git/operations.js.map +1 -1
  135. package/dist/handlers/legacyBusAdapter.d.ts +1 -1
  136. package/dist/handlers/legacyBusAdapter.d.ts.map +1 -1
  137. package/dist/handlers/legacyBusAdapter.js +13 -1
  138. package/dist/handlers/legacyBusAdapter.js.map +1 -1
  139. package/dist/handlers/messageHandler.d.ts +23 -0
  140. package/dist/handlers/messageHandler.d.ts.map +1 -1
  141. package/dist/handlers/messageHandler.js +226 -14
  142. package/dist/handlers/messageHandler.js.map +1 -1
  143. package/dist/index.js +42 -2
  144. package/dist/index.js.map +1 -1
  145. package/dist/messageBus/busServerTransport.d.ts.map +1 -1
  146. package/dist/messageBus/busServerTransport.js +2 -0
  147. package/dist/messageBus/busServerTransport.js.map +1 -1
  148. package/dist/service/debugHttpServer.d.ts.map +1 -1
  149. package/dist/service/debugHttpServer.js +2 -0
  150. package/dist/service/debugHttpServer.js.map +1 -1
  151. package/dist/service/ensureDaemon.d.ts.map +1 -1
  152. package/dist/service/ensureDaemon.js +27 -2
  153. package/dist/service/ensureDaemon.js.map +1 -1
  154. package/dist/service/index.d.ts.map +1 -1
  155. package/dist/service/index.js +2 -0
  156. package/dist/service/index.js.map +1 -1
  157. package/dist/service/ipcClient.d.ts.map +1 -1
  158. package/dist/service/ipcClient.js +3 -1
  159. package/dist/service/ipcClient.js.map +1 -1
  160. package/dist/service/ipcServer.d.ts.map +1 -1
  161. package/dist/service/ipcServer.js +2 -0
  162. package/dist/service/ipcServer.js.map +1 -1
  163. package/dist/service/pushClient.d.ts.map +1 -1
  164. package/dist/service/pushClient.js +2 -0
  165. package/dist/service/pushClient.js.map +1 -1
  166. package/dist/service/run.d.ts.map +1 -1
  167. package/dist/service/run.js +14 -1
  168. package/dist/service/run.js.map +1 -1
  169. package/dist/service/singleton.d.ts +1 -0
  170. package/dist/service/singleton.d.ts.map +1 -1
  171. package/dist/service/singleton.js +5 -0
  172. package/dist/service/singleton.js.map +1 -1
  173. package/dist/service/stateStore.d.ts.map +1 -1
  174. package/dist/service/stateStore.js +2 -0
  175. package/dist/service/stateStore.js.map +1 -1
  176. package/dist/service/systemdUnit.d.ts +149 -0
  177. package/dist/service/systemdUnit.d.ts.map +1 -0
  178. package/dist/service/systemdUnit.js +322 -0
  179. package/dist/service/systemdUnit.js.map +1 -0
  180. package/dist/service/types.d.ts +6 -0
  181. package/dist/service/types.d.ts.map +1 -1
  182. package/dist/service/types.js +3 -1
  183. package/dist/service/types.js.map +1 -1
  184. package/dist/storage/eventStore.d.ts.map +1 -1
  185. package/dist/storage/eventStore.js +2 -0
  186. package/dist/storage/eventStore.js.map +1 -1
  187. package/dist/terminal/terminalManager.d.ts.map +1 -1
  188. package/dist/terminal/terminalManager.js +2 -0
  189. package/dist/terminal/terminalManager.js.map +1 -1
  190. package/dist/tombstoneCheck.d.ts.map +1 -1
  191. package/dist/tombstoneCheck.js +2 -0
  192. package/dist/tombstoneCheck.js.map +1 -1
  193. package/package.json +3 -3
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 Quicksave, KingYoung Technology
3
+ Copyright (c) 2026 King Young Technology and contributors
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,114 +1,52 @@
1
1
  # @sumicom/quicksave — desktop agent
2
2
 
3
- Background daemon that runs on your development machine and connects your
4
- Quicksave PWA (phone or browser) to the local git working tree and Claude
5
- Code CLI sessions. All communication is end-to-end encrypted; the relay
6
- server never sees plaintext.
3
+ Background daemon that runs on your development machine and connects
4
+ your Quicksave PWA (phone or browser) to your coding-agent CLI sessions
5
+ and the local git working tree. All communication is end-to-end
6
+ encrypted; the relay server never sees plaintext.
7
7
 
8
- ## Install
8
+ ## Prerequisites
9
9
 
10
- ```bash
11
- npm install -g @sumicom/quicksave
12
- ```
13
-
14
- ## Usage
10
+ Quicksave drives a coding-agent CLI on your `$PATH`. Install one (or
11
+ both) and sign in first:
15
12
 
16
13
  ```bash
17
- # cd into a git repo, then:
18
- quicksave
19
-
20
- # or point at an explicit repo:
21
- quicksave --repo /path/to/repo
14
+ # Claude Code (Anthropic) https://code.claude.com/docs/en/setup
15
+ curl -fsSL https://claude.ai/install.sh | bash
16
+ claude # follow login prompts
22
17
 
23
- # custom signaling server:
24
- quicksave -s wss://your-relay.example.com
18
+ # Codex (OpenAI) — https://github.com/openai/codex#readme
19
+ npm install -g @openai/codex
20
+ codex # follow login prompts
25
21
  ```
26
22
 
27
- On first run this prints a pairing URL and QR code. Scan it from the
28
- [Quicksave PWA](https://quicksave.dev) to connect.
29
-
30
- The CLI auto-launches a background daemon (`quicksave service run`) and
31
- then exits. The daemon keeps running; future `quicksave` invocations just
32
- attach to it to add repos or re-read pairing info.
33
-
34
- ## Subcommands
35
-
36
- | Command | Purpose |
37
- | -------------------------------- | ------------------------------------------------------------ |
38
- | `quicksave` | Start / ensure daemon; print pairing URL + QR |
39
- | `quicksave rotate-keys` | Generate a new keypair (invalidates paired PWAs) |
40
- | `quicksave service start` | Start daemon in the background |
41
- | `quicksave service stop` | Stop the running daemon |
42
- | `quicksave service status` | Live status (pid, peers, active sessions, managed repos) |
43
- | `quicksave service info` | Static status from `service.json` (no IPC) |
44
- | `quicksave service run` | Run daemon in foreground (not normally invoked directly) |
45
-
46
- ### Debug commands
23
+ You also need Node.js 20+ and `git`.
47
24
 
48
- Available only when `QUICKSAVE_DEBUG=1` is set (auto-enabled in dev mode):
25
+ ## Install
49
26
 
50
27
  ```bash
51
- QUICKSAVE_DEBUG=1 quicksave service debug # full state snapshot
52
- QUICKSAVE_DEBUG=1 quicksave service sessions # list all Claude sessions
53
- QUICKSAVE_DEBUG=1 quicksave service cards <id> # card history for a session
54
- QUICKSAVE_DEBUG=1 quicksave service resolve <id> # force-resolve stuck permission
28
+ npm install -g @sumicom/quicksave
55
29
  ```
56
30
 
57
- ## What the daemon does
58
-
59
- - Maintains the WebSocket connection to the relay, does NaCl handshake with
60
- each paired PWA, and decrypts/dispatches every message.
61
- - Executes git operations against managed repos (`status`, `diff`, stage,
62
- unstage, commit, branch, log, `.gitignore` edits, etc.).
63
- - Manages Claude Code CLI sessions: spawns `claude` with stream-json I/O,
64
- parses the output into `Card` events, handles `control_request`
65
- permission prompts, forwards them to the PWA for approval, and streams
66
- card updates back.
67
- - Generates commit messages either via Anthropic API (user-supplied key)
68
- or via an agentic `claude -p` loop that uses the user's local Claude
69
- subscription.
70
- - Hosts a pool of PTY-backed interactive shells (`src/terminal/terminalManager.ts`,
71
- via `node-pty`) that the PWA can drive remotely. Each terminal keeps a
72
- bounded scrollback buffer so a reconnecting PWA can redraw the current
73
- screen without the shell having to repaint.
74
- - Serves an IPC (Unix socket) JSON-RPC API for the CLI to query state and
75
- issue debug commands.
76
- - Triggers Web Push notifications through the relay when a permission
77
- prompt arrives and no PWA peer is currently subscribed.
78
-
79
- ## Architecture
80
-
81
- See [`docs/references/quicksave-architecture.md`](../../docs/references/quicksave-architecture.md)
82
- for the full design — session lifecycle, MessageBus wiring, permission
83
- flow, commit-summary pipeline, IPC protocol.
84
-
85
- ## Data location
86
-
87
- | Path | Contents |
88
- | ------------------------------- | ------------------------------------------------- |
89
- | `~/.quicksave/config.json` | Agent ID, NaCl keypairs, managed repo list |
90
- | `~/.quicksave/run/service.json` | Live daemon state (pid, socket path, heartbeat) |
91
- | `~/.quicksave/run/*.sock` | IPC socket |
92
- | `~/.quicksave/sessions/` | Per-session JSONL history (Claude Code CLI) |
93
-
94
- ## Development
31
+ ## Quick start
95
32
 
96
33
  ```bash
97
- # from monorepo root
98
- pnpm --filter @sumicom/quicksave dev # tsx watch
99
- pnpm --filter @sumicom/quicksave test # vitest
100
- pnpm --filter @sumicom/quicksave build # tsc
34
+ cd /path/to/your/repo
35
+ quicksave
101
36
  ```
102
37
 
103
- Fast restart-from-source during active development:
38
+ On first run this prints a pairing URL and QR code. Scan it from the
39
+ [Quicksave PWA](https://quicksave.dev) to connect.
104
40
 
105
- ```bash
106
- ./scripts/dev-daemon.sh # kill + spawn
107
- ./scripts/dev-daemon-delayed.sh 30 # delayed variant safe
108
- # when called from inside a
109
- # daemon-spawned Claude CLI
110
- ```
41
+ The CLI auto-launches a background daemon (`quicksave service run`) and
42
+ then exits. The daemon keeps running; future `quicksave` invocations
43
+ just attach to it to add repos or re-read pairing info.
111
44
 
112
- ## License
45
+ ## More
113
46
 
114
- MIT
47
+ - **CLI subcommands, debug commands, data layout** —
48
+ [`docs/references/agent-cli.md`](../../docs/references/agent-cli.md)
49
+ - **Architecture (sessions, MessageBus, permissions, IPC)** —
50
+ [`docs/references/quicksave-architecture.en.md`](../../docs/references/quicksave-architecture.en.md)
51
+ - **Local development** —
52
+ [`docs/development.md`](../../docs/development.md)
@@ -1 +1 @@
1
- {"version":3,"file":"asyncQueue.d.ts","sourceRoot":"","sources":["../../src/ai/asyncQueue.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,qBAAa,UAAU,CAAC,CAAC,CAAE,YAAW,aAAa,CAAC,CAAC,CAAC;IACpD,OAAO,CAAC,KAAK,CAAW;IACxB,OAAO,CAAC,OAAO,CAAqD;IACpE,OAAO,CAAC,IAAI,CAAS;IAErB,8EAA8E;IAC9E,IAAI,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI;IAWpB,iDAAiD;IACjD,GAAG,IAAI,IAAI;IASX,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC;CAe3C"}
1
+ {"version":3,"file":"asyncQueue.d.ts","sourceRoot":"","sources":["../../src/ai/asyncQueue.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,qBAAa,UAAU,CAAC,CAAC,CAAE,YAAW,aAAa,CAAC,CAAC,CAAC;IACpD,OAAO,CAAC,KAAK,CAAW;IACxB,OAAO,CAAC,OAAO,CAAqD;IACpE,OAAO,CAAC,IAAI,CAAS;IAErB,8EAA8E;IAC9E,IAAI,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI;IAWpB,iDAAiD;IACjD,GAAG,IAAI,IAAI;IASX,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC;CAe3C"}
@@ -1,3 +1,5 @@
1
+ // SPDX-FileCopyrightText: 2026 King Young Technology
2
+ // SPDX-License-Identifier: MIT
1
3
  /**
2
4
  * A simple async iterable queue for multi-turn prompt delivery.
3
5
  *
@@ -1 +1 @@
1
- {"version":3,"file":"asyncQueue.js","sourceRoot":"","sources":["../../src/ai/asyncQueue.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,OAAO,UAAU;IACb,KAAK,GAAQ,EAAE,CAAC;IAChB,OAAO,GAAgD,IAAI,CAAC;IAC5D,IAAI,GAAG,KAAK,CAAC;IAErB,8EAA8E;IAC9E,IAAI,CAAC,KAAQ;QACX,IAAI,IAAI,CAAC,IAAI;YAAE,OAAO;QACtB,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;YAC7B,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;YACpB,OAAO,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAClC,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IAED,iDAAiD;IACjD,GAAG;QACD,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;YAC7B,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;YACpB,OAAO,CAAC,EAAE,KAAK,EAAE,SAAgB,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;IAED,CAAC,MAAM,CAAC,aAAa,CAAC;QACpB,OAAO;YACL,IAAI,EAAE,GAA+B,EAAE;gBACrC,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBAC1B,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,EAAG,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;gBACtE,CAAC;gBACD,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;oBACd,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,SAAgB,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;gBAClE,CAAC;gBACD,OAAO,IAAI,OAAO,CAAoB,CAAC,OAAO,EAAE,EAAE;oBAChD,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;gBACzB,CAAC,CAAC,CAAC;YACL,CAAC;SACF,CAAC;IACJ,CAAC;CACF"}
1
+ {"version":3,"file":"asyncQueue.js","sourceRoot":"","sources":["../../src/ai/asyncQueue.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,+BAA+B;AAC/B;;;;;GAKG;AACH,MAAM,OAAO,UAAU;IACb,KAAK,GAAQ,EAAE,CAAC;IAChB,OAAO,GAAgD,IAAI,CAAC;IAC5D,IAAI,GAAG,KAAK,CAAC;IAErB,8EAA8E;IAC9E,IAAI,CAAC,KAAQ;QACX,IAAI,IAAI,CAAC,IAAI;YAAE,OAAO;QACtB,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;YAC7B,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;YACpB,OAAO,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAClC,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IAED,iDAAiD;IACjD,GAAG;QACD,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;YAC7B,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;YACpB,OAAO,CAAC,EAAE,KAAK,EAAE,SAAgB,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;IAED,CAAC,MAAM,CAAC,aAAa,CAAC;QACpB,OAAO;YACL,IAAI,EAAE,GAA+B,EAAE;gBACrC,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBAC1B,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,EAAG,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;gBACtE,CAAC;gBACD,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;oBACd,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,SAAgB,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;gBAClE,CAAC;gBACD,OAAO,IAAI,OAAO,CAAoB,CAAC,OAAO,EAAE,EAAE;oBAChD,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;gBACzB,CAAC,CAAC,CAAC;YACL,CAAC;SACF,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,71 @@
1
+ import { type Attachment, type AttachmentUploadRequestPayload, type AttachmentUploadResponsePayload } from '@sumicom/quicksave-shared';
2
+ export interface AttachmentNotFoundError extends Error {
3
+ code: 'attachment_not_found';
4
+ attachmentId: string;
5
+ }
6
+ export interface AttachmentNotReadyError extends Error {
7
+ code: 'attachment_not_ready';
8
+ attachmentId: string;
9
+ }
10
+ export interface AttachmentTooLargeError extends Error {
11
+ code: 'attachment_too_large';
12
+ attachmentId: string;
13
+ }
14
+ export interface AttachmentTooManyPagesError extends Error {
15
+ code: 'attachment_too_many_pages';
16
+ attachmentId: string;
17
+ }
18
+ export interface AttachmentBadRequestError extends Error {
19
+ code: 'attachment_bad_request';
20
+ attachmentId: string;
21
+ }
22
+ export interface AttachmentPeerQuotaError extends Error {
23
+ code: 'attachment_peer_quota';
24
+ }
25
+ export interface AttachmentStagingOptions {
26
+ /** Override TTL — primarily for tests. */
27
+ ttlMs?: number;
28
+ /** Override per-peer cap — primarily for tests. */
29
+ perPeerMaxBytes?: number;
30
+ /** Custom clock — primarily for tests. */
31
+ now?: () => number;
32
+ }
33
+ /**
34
+ * In-memory staging map keyed by `(peerAddress, attachmentId)`. One instance
35
+ * per agent process; the messageHandler injects the peer address on every
36
+ * request.
37
+ */
38
+ export declare class AttachmentStaging {
39
+ private readonly byPeer;
40
+ private readonly perPeerBytes;
41
+ private readonly ttlMs;
42
+ private readonly perPeerMaxBytes;
43
+ private readonly now;
44
+ constructor(opts?: AttachmentStagingOptions);
45
+ /**
46
+ * Apply one upload chunk. Returns the running progress + ready flag.
47
+ * Throws a tagged Error (`code` field) for validation failures so the
48
+ * messageHandler can map them to bus error responses.
49
+ */
50
+ acceptChunk(peerAddress: string, payload: AttachmentUploadRequestPayload): AttachmentUploadResponsePayload;
51
+ /**
52
+ * Atomically remove and return the requested attachments. Throws on the
53
+ * first missing or not-ready id (with that id in the error). On error,
54
+ * **no** attachments are consumed — callers can retry safely.
55
+ */
56
+ consume(peerAddress: string, attachmentIds: readonly string[]): Attachment[];
57
+ /** Drop a staged attachment if present. Returns true if anything was removed. */
58
+ cancel(peerAddress: string, attachmentId: string): boolean;
59
+ /** Drop every staging record for a peer (called on disconnect). */
60
+ removePeer(peerAddress: string): void;
61
+ /** Sweep expired entries. Safe to call from a periodic timer. */
62
+ gc(): void;
63
+ /** Test/diagnostic: count of staged records for a peer. */
64
+ size(peerAddress: string): number;
65
+ /** Test/diagnostic: bytes currently held for a peer. */
66
+ bytesFor(peerAddress: string): number;
67
+ private validateMeta;
68
+ private isReady;
69
+ private dropEntry;
70
+ }
71
+ //# sourceMappingURL=attachmentStaging.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attachmentStaging.d.ts","sourceRoot":"","sources":["../../src/ai/attachmentStaging.ts"],"names":[],"mappings":"AASA,OAAO,EAKL,KAAK,UAAU,EAEf,KAAK,8BAA8B,EACnC,KAAK,+BAA+B,EACrC,MAAM,2BAA2B,CAAC;AAqBnC,MAAM,WAAW,uBAAwB,SAAQ,KAAK;IACpD,IAAI,EAAE,sBAAsB,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;CACtB;AACD,MAAM,WAAW,uBAAwB,SAAQ,KAAK;IACpD,IAAI,EAAE,sBAAsB,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;CACtB;AACD,MAAM,WAAW,uBAAwB,SAAQ,KAAK;IACpD,IAAI,EAAE,sBAAsB,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;CACtB;AACD,MAAM,WAAW,2BAA4B,SAAQ,KAAK;IACxD,IAAI,EAAE,2BAA2B,CAAC;IAClC,YAAY,EAAE,MAAM,CAAC;CACtB;AACD,MAAM,WAAW,yBAA0B,SAAQ,KAAK;IACtD,IAAI,EAAE,wBAAwB,CAAC;IAC/B,YAAY,EAAE,MAAM,CAAC;CACtB;AACD,MAAM,WAAW,wBAAyB,SAAQ,KAAK;IACrD,IAAI,EAAE,uBAAuB,CAAC;CAC/B;AAqBD,MAAM,WAAW,wBAAwB;IACvC,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,0CAA0C;IAC1C,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoD;IAC3E,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA6B;IAC1D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;gBAEvB,IAAI,GAAE,wBAA6B;IAM/C;;;;OAIG;IACH,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,8BAA8B,GAAG,+BAA+B;IA2G1G;;;;OAIG;IACH,OAAO,CAAC,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,SAAS,MAAM,EAAE,GAAG,UAAU,EAAE;IA6B5E,iFAAiF;IACjF,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO;IAI1D,mEAAmE;IACnE,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAKrC,iEAAiE;IACjE,EAAE,IAAI,IAAI;IAeV,2DAA2D;IAC3D,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM;IAIjC,wDAAwD;IACxD,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM;IAMrC,OAAO,CAAC,YAAY;IAqBpB,OAAO,CAAC,OAAO;IAIf,OAAO,CAAC,SAAS;CAiBlB"}
@@ -0,0 +1,243 @@
1
+ // SPDX-FileCopyrightText: 2026 King Young Technology
2
+ // SPDX-License-Identifier: MIT
3
+ // ============================================================================
4
+ // Attachment staging — buffers chunked uploads from a peer until they are
5
+ // consumed by a `claude:start` / `claude:resume` whose payload references
6
+ // the attachment ids. Buffers live in memory; the GC loop drops idle records
7
+ // after `ATTACHMENT_STAGING_TTL_MS`.
8
+ // ============================================================================
9
+ import { ATTACHMENT_LIMITS, ATTACHMENT_STAGING_TTL_MS, PDF_MAX_PAGES, PER_PEER_STAGING_MAX_BYTES, } from '@sumicom/quicksave-shared';
10
+ import { countPdfPages } from './pdfMeta.js';
11
+ function makeError(code, message, extra = {}) {
12
+ const err = new Error(message);
13
+ err.code = code;
14
+ Object.assign(err, extra);
15
+ return err;
16
+ }
17
+ /**
18
+ * In-memory staging map keyed by `(peerAddress, attachmentId)`. One instance
19
+ * per agent process; the messageHandler injects the peer address on every
20
+ * request.
21
+ */
22
+ export class AttachmentStaging {
23
+ byPeer = new Map();
24
+ perPeerBytes = new Map();
25
+ ttlMs;
26
+ perPeerMaxBytes;
27
+ now;
28
+ constructor(opts = {}) {
29
+ this.ttlMs = opts.ttlMs ?? ATTACHMENT_STAGING_TTL_MS;
30
+ this.perPeerMaxBytes = opts.perPeerMaxBytes ?? PER_PEER_STAGING_MAX_BYTES;
31
+ this.now = opts.now ?? (() => Date.now());
32
+ }
33
+ /**
34
+ * Apply one upload chunk. Returns the running progress + ready flag.
35
+ * Throws a tagged Error (`code` field) for validation failures so the
36
+ * messageHandler can map them to bus error responses.
37
+ */
38
+ acceptChunk(peerAddress, payload) {
39
+ const { attachmentId, chunkIndex, chunk, meta } = payload;
40
+ if (!attachmentId || typeof attachmentId !== 'string') {
41
+ throw makeError('attachment_bad_request', 'attachmentId required', { attachmentId: String(attachmentId) });
42
+ }
43
+ if (typeof chunkIndex !== 'number' || chunkIndex < 0 || !Number.isInteger(chunkIndex)) {
44
+ throw makeError('attachment_bad_request', 'chunkIndex must be a non-negative integer', { attachmentId });
45
+ }
46
+ if (typeof chunk !== 'string') {
47
+ throw makeError('attachment_bad_request', 'chunk must be base64 string', { attachmentId });
48
+ }
49
+ const peerMap = this.byPeer.get(peerAddress) ?? new Map();
50
+ let staged = peerMap.get(attachmentId);
51
+ if (!staged) {
52
+ // First time we see this id — meta is required.
53
+ if (!meta) {
54
+ throw makeError('attachment_bad_request', 'meta required on first chunk', { attachmentId });
55
+ }
56
+ this.validateMeta(attachmentId, meta);
57
+ staged = {
58
+ attachmentId,
59
+ kind: meta.kind,
60
+ mimeType: meta.mimeType,
61
+ name: meta.name,
62
+ size: meta.size,
63
+ totalChunks: meta.totalChunks,
64
+ chunks: new Array(meta.totalChunks),
65
+ receivedBytes: 0,
66
+ receivedCount: 0,
67
+ lastTouchedAt: this.now(),
68
+ };
69
+ peerMap.set(attachmentId, staged);
70
+ this.byPeer.set(peerAddress, peerMap);
71
+ }
72
+ else if (meta) {
73
+ // Subsequent chunks may include meta but must be consistent.
74
+ if (meta.kind !== staged.kind
75
+ || meta.mimeType !== staged.mimeType
76
+ || meta.name !== staged.name
77
+ || meta.size !== staged.size
78
+ || meta.totalChunks !== staged.totalChunks) {
79
+ throw makeError('attachment_bad_request', 'meta mismatch for existing attachment', { attachmentId });
80
+ }
81
+ }
82
+ if (chunkIndex >= staged.totalChunks) {
83
+ throw makeError('attachment_bad_request', `chunkIndex ${chunkIndex} >= totalChunks ${staged.totalChunks}`, { attachmentId });
84
+ }
85
+ if (staged.chunks[chunkIndex] !== undefined) {
86
+ // Idempotent re-send: if the bytes match, no-op; otherwise reject.
87
+ const existing = staged.chunks[chunkIndex];
88
+ const incoming = Buffer.from(chunk, 'base64');
89
+ if (existing.equals(incoming)) {
90
+ staged.lastTouchedAt = this.now();
91
+ return { attachmentId, receivedBytes: staged.receivedBytes, ready: this.isReady(staged) };
92
+ }
93
+ throw makeError('attachment_bad_request', `chunk ${chunkIndex} already received with different bytes`, { attachmentId });
94
+ }
95
+ const buf = Buffer.from(chunk, 'base64');
96
+ const nextRunningBytes = staged.receivedBytes + buf.length;
97
+ if (nextRunningBytes > staged.size) {
98
+ throw makeError('attachment_too_large', `running bytes exceeds declared size`, { attachmentId });
99
+ }
100
+ const peerTotal = this.perPeerBytes.get(peerAddress) ?? 0;
101
+ if (peerTotal + buf.length > this.perPeerMaxBytes) {
102
+ throw makeError('attachment_peer_quota', 'per-peer staging quota exceeded');
103
+ }
104
+ staged.chunks[chunkIndex] = buf;
105
+ staged.receivedBytes = nextRunningBytes;
106
+ staged.receivedCount += 1;
107
+ staged.lastTouchedAt = this.now();
108
+ this.perPeerBytes.set(peerAddress, peerTotal + buf.length);
109
+ const ready = this.isReady(staged);
110
+ if (ready && staged.receivedBytes !== staged.size) {
111
+ // Strict: declared size must match received bytes when ready.
112
+ this.dropEntry(peerAddress, attachmentId);
113
+ throw makeError('attachment_bad_request', 'received bytes do not match declared size', { attachmentId });
114
+ }
115
+ // Page-count check on PDF completion. The Anthropic API rejects PDFs over
116
+ // PDF_MAX_PAGES and the SDK rewrites the rejection as a poison "PDF too
117
+ // large" assistant block — drop the entry now so the user sees a clean
118
+ // error instead of a stuck session. countPdfPages returns null/0 for
119
+ // PDFs where pages live in compressed object streams (rare); in that
120
+ // case we let it through and fall back to the recovery card path.
121
+ if (ready && staged.kind === 'pdf') {
122
+ const pages = countPdfPages(Buffer.concat(staged.chunks));
123
+ if (pages !== null && pages > PDF_MAX_PAGES) {
124
+ this.dropEntry(peerAddress, attachmentId);
125
+ throw makeError('attachment_too_many_pages', `PDF has ${pages} pages (max ${PDF_MAX_PAGES})`, { attachmentId });
126
+ }
127
+ }
128
+ return { attachmentId, receivedBytes: staged.receivedBytes, ready };
129
+ }
130
+ /**
131
+ * Atomically remove and return the requested attachments. Throws on the
132
+ * first missing or not-ready id (with that id in the error). On error,
133
+ * **no** attachments are consumed — callers can retry safely.
134
+ */
135
+ consume(peerAddress, attachmentIds) {
136
+ if (attachmentIds.length === 0)
137
+ return [];
138
+ const peerMap = this.byPeer.get(peerAddress);
139
+ if (!peerMap) {
140
+ throw makeError('attachment_not_found', `no staged attachments for peer`, { attachmentId: attachmentIds[0] });
141
+ }
142
+ // Validate all first, then remove.
143
+ for (const id of attachmentIds) {
144
+ const s = peerMap.get(id);
145
+ if (!s)
146
+ throw makeError('attachment_not_found', `attachment ${id} not staged`, { attachmentId: id });
147
+ if (!this.isReady(s))
148
+ throw makeError('attachment_not_ready', `attachment ${id} not ready`, { attachmentId: id });
149
+ }
150
+ const result = [];
151
+ for (const id of attachmentIds) {
152
+ const s = peerMap.get(id);
153
+ const data = Buffer.concat(s.chunks).toString('base64');
154
+ result.push({
155
+ id: s.attachmentId,
156
+ kind: s.kind,
157
+ mimeType: s.mimeType,
158
+ name: s.name,
159
+ size: s.size,
160
+ data,
161
+ });
162
+ this.dropEntry(peerAddress, id);
163
+ }
164
+ return result;
165
+ }
166
+ /** Drop a staged attachment if present. Returns true if anything was removed. */
167
+ cancel(peerAddress, attachmentId) {
168
+ return this.dropEntry(peerAddress, attachmentId);
169
+ }
170
+ /** Drop every staging record for a peer (called on disconnect). */
171
+ removePeer(peerAddress) {
172
+ this.byPeer.delete(peerAddress);
173
+ this.perPeerBytes.delete(peerAddress);
174
+ }
175
+ /** Sweep expired entries. Safe to call from a periodic timer. */
176
+ gc() {
177
+ const cutoff = this.now() - this.ttlMs;
178
+ for (const [peer, map] of this.byPeer) {
179
+ for (const [id, s] of map) {
180
+ if (s.lastTouchedAt <= cutoff) {
181
+ this.dropEntry(peer, id);
182
+ }
183
+ }
184
+ if (map.size === 0) {
185
+ this.byPeer.delete(peer);
186
+ this.perPeerBytes.delete(peer);
187
+ }
188
+ }
189
+ }
190
+ /** Test/diagnostic: count of staged records for a peer. */
191
+ size(peerAddress) {
192
+ return this.byPeer.get(peerAddress)?.size ?? 0;
193
+ }
194
+ /** Test/diagnostic: bytes currently held for a peer. */
195
+ bytesFor(peerAddress) {
196
+ return this.perPeerBytes.get(peerAddress) ?? 0;
197
+ }
198
+ // ── Private ─────────────────────────────────────────────────────────────
199
+ validateMeta(attachmentId, meta) {
200
+ if (!meta.kind || !ATTACHMENT_LIMITS[meta.kind]) {
201
+ throw makeError('attachment_bad_request', `unknown kind`, { attachmentId });
202
+ }
203
+ if (typeof meta.size !== 'number' || meta.size <= 0 || !Number.isFinite(meta.size)) {
204
+ throw makeError('attachment_bad_request', 'invalid size', { attachmentId });
205
+ }
206
+ if (meta.size > ATTACHMENT_LIMITS[meta.kind].maxBytes) {
207
+ throw makeError('attachment_too_large', `${meta.kind} exceeds ${ATTACHMENT_LIMITS[meta.kind].maxBytes} bytes`, { attachmentId });
208
+ }
209
+ if (typeof meta.totalChunks !== 'number' || meta.totalChunks <= 0 || !Number.isInteger(meta.totalChunks)) {
210
+ throw makeError('attachment_bad_request', 'invalid totalChunks', { attachmentId });
211
+ }
212
+ if (typeof meta.mimeType !== 'string' || meta.mimeType.length === 0) {
213
+ throw makeError('attachment_bad_request', 'invalid mimeType', { attachmentId });
214
+ }
215
+ if (typeof meta.name !== 'string' || meta.name.length === 0) {
216
+ throw makeError('attachment_bad_request', 'invalid name', { attachmentId });
217
+ }
218
+ }
219
+ isReady(s) {
220
+ return s.receivedCount === s.totalChunks;
221
+ }
222
+ dropEntry(peerAddress, attachmentId) {
223
+ const peerMap = this.byPeer.get(peerAddress);
224
+ if (!peerMap)
225
+ return false;
226
+ const s = peerMap.get(attachmentId);
227
+ if (!s)
228
+ return false;
229
+ peerMap.delete(attachmentId);
230
+ const remainingBytes = (this.perPeerBytes.get(peerAddress) ?? 0) - s.receivedBytes;
231
+ if (remainingBytes > 0) {
232
+ this.perPeerBytes.set(peerAddress, remainingBytes);
233
+ }
234
+ else {
235
+ this.perPeerBytes.delete(peerAddress);
236
+ }
237
+ if (peerMap.size === 0) {
238
+ this.byPeer.delete(peerAddress);
239
+ }
240
+ return true;
241
+ }
242
+ }
243
+ //# sourceMappingURL=attachmentStaging.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attachmentStaging.js","sourceRoot":"","sources":["../../src/ai/attachmentStaging.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,+BAA+B;AAC/B,+EAA+E;AAC/E,0EAA0E;AAC1E,0EAA0E;AAC1E,6EAA6E;AAC7E,qCAAqC;AACrC,+EAA+E;AAE/E,OAAO,EACL,iBAAiB,EACjB,yBAAyB,EACzB,aAAa,EACb,0BAA0B,GAK3B,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAoD7C,SAAS,SAAS,CAChB,IAAO,EACP,OAAe,EACf,QAA+B,EAAE;IAEjC,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,OAAO,CAAwB,CAAC;IACrD,GAA8B,CAAC,IAAI,GAAG,IAAI,CAAC;IAC5C,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAC1B,OAAO,GAAG,CAAC;AACb,CAAC;AAWD;;;;GAIG;AACH,MAAM,OAAO,iBAAiB;IACX,MAAM,GAAG,IAAI,GAAG,EAAyC,CAAC;IAC1D,YAAY,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,KAAK,CAAS;IACd,eAAe,CAAS;IACxB,GAAG,CAAe;IAEnC,YAAY,OAAiC,EAAE;QAC7C,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,yBAAyB,CAAC;QACrD,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,eAAe,IAAI,0BAA0B,CAAC;QAC1E,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IAC5C,CAAC;IAED;;;;OAIG;IACH,WAAW,CAAC,WAAmB,EAAE,OAAuC;QACtE,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC;QAC1D,IAAI,CAAC,YAAY,IAAI,OAAO,YAAY,KAAK,QAAQ,EAAE,CAAC;YACtD,MAAM,SAAS,CAAC,wBAAwB,EAAE,uBAAuB,EAAE,EAAE,YAAY,EAAE,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;QAC7G,CAAC;QACD,IAAI,OAAO,UAAU,KAAK,QAAQ,IAAI,UAAU,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,EAAE,CAAC;YACtF,MAAM,SAAS,CAAC,wBAAwB,EAAE,2CAA2C,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;QAC3G,CAAC;QACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC9B,MAAM,SAAS,CAAC,wBAAwB,EAAE,6BAA6B,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;QAC7F,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,IAAI,GAAG,EAA4B,CAAC;QACpF,IAAI,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QAEvC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,gDAAgD;YAChD,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,MAAM,SAAS,CAAC,wBAAwB,EAAE,8BAA8B,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;YAC9F,CAAC;YACD,IAAI,CAAC,YAAY,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;YACtC,MAAM,GAAG;gBACP,YAAY;gBACZ,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,WAAW,EAAE,IAAI,CAAC,WAAW;gBAC7B,MAAM,EAAE,IAAI,KAAK,CAAqB,IAAI,CAAC,WAAW,CAAC;gBACvD,aAAa,EAAE,CAAC;gBAChB,aAAa,EAAE,CAAC;gBAChB,aAAa,EAAE,IAAI,CAAC,GAAG,EAAE;aAC1B,CAAC;YACF,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;YAClC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QACxC,CAAC;aAAM,IAAI,IAAI,EAAE,CAAC;YAChB,6DAA6D;YAC7D,IACE,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,IAAI;mBACtB,IAAI,CAAC,QAAQ,KAAK,MAAM,CAAC,QAAQ;mBACjC,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,IAAI;mBACzB,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,IAAI;mBACzB,IAAI,CAAC,WAAW,KAAK,MAAM,CAAC,WAAW,EAC1C,CAAC;gBACD,MAAM,SAAS,CAAC,wBAAwB,EAAE,uCAAuC,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;YACvG,CAAC;QACH,CAAC;QAED,IAAI,UAAU,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;YACrC,MAAM,SAAS,CAAC,wBAAwB,EAAE,cAAc,UAAU,mBAAmB,MAAM,CAAC,WAAW,EAAE,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;QAC/H,CAAC;QACD,IAAI,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,SAAS,EAAE,CAAC;YAC5C,mEAAmE;YACnE,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,UAAU,CAAE,CAAC;YAC5C,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;YAC9C,IAAI,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC9B,MAAM,CAAC,aAAa,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;gBAClC,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,CAAC,aAAa,EAAE,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAC5F,CAAC;YACD,MAAM,SAAS,CAAC,wBAAwB,EAAE,SAAS,UAAU,wCAAwC,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;QAC3H,CAAC;QAED,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QACzC,MAAM,gBAAgB,GAAG,MAAM,CAAC,aAAa,GAAG,GAAG,CAAC,MAAM,CAAC;QAC3D,IAAI,gBAAgB,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;YACnC,MAAM,SAAS,CAAC,sBAAsB,EAAE,qCAAqC,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;QACnG,CAAC;QAED,MAAM,SAAS,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QAC1D,IAAI,SAAS,GAAG,GAAG,CAAC,MAAM,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;YAClD,MAAM,SAAS,CAAC,uBAAuB,EAAE,iCAAiC,CAAC,CAAC;QAC9E,CAAC;QAED,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,GAAG,CAAC;QAChC,MAAM,CAAC,aAAa,GAAG,gBAAgB,CAAC;QACxC,MAAM,CAAC,aAAa,IAAI,CAAC,CAAC;QAC1B,MAAM,CAAC,aAAa,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAClC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,SAAS,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC;QAE3D,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACnC,IAAI,KAAK,IAAI,MAAM,CAAC,aAAa,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC;YAClD,8DAA8D;YAC9D,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;YAC1C,MAAM,SAAS,CAAC,wBAAwB,EAAE,2CAA2C,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;QAC3G,CAAC;QAED,0EAA0E;QAC1E,wEAAwE;QACxE,uEAAuE;QACvE,qEAAqE;QACrE,qEAAqE;QACrE,kEAAkE;QAClE,IAAI,KAAK,IAAI,MAAM,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;YACnC,MAAM,KAAK,GAAG,aAAa,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAkB,CAAC,CAAC,CAAC;YACtE,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,GAAG,aAAa,EAAE,CAAC;gBAC5C,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;gBAC1C,MAAM,SAAS,CACb,2BAA2B,EAC3B,WAAW,KAAK,eAAe,aAAa,GAAG,EAC/C,EAAE,YAAY,EAAE,CACjB,CAAC;YACJ,CAAC;QACH,CAAC;QAED,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,CAAC,aAAa,EAAE,KAAK,EAAE,CAAC;IACtE,CAAC;IAED;;;;OAIG;IACH,OAAO,CAAC,WAAmB,EAAE,aAAgC;QAC3D,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QAC1C,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAC7C,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,SAAS,CAAC,sBAAsB,EAAE,gCAAgC,EAAE,EAAE,YAAY,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAChH,CAAC;QACD,mCAAmC;QACnC,KAAK,MAAM,EAAE,IAAI,aAAa,EAAE,CAAC;YAC/B,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC1B,IAAI,CAAC,CAAC;gBAAE,MAAM,SAAS,CAAC,sBAAsB,EAAE,cAAc,EAAE,aAAa,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,CAAC,CAAC;YACrG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;gBAAE,MAAM,SAAS,CAAC,sBAAsB,EAAE,cAAc,EAAE,YAAY,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,CAAC,CAAC;QACpH,CAAC;QACD,MAAM,MAAM,GAAiB,EAAE,CAAC;QAChC,KAAK,MAAM,EAAE,IAAI,aAAa,EAAE,CAAC;YAC/B,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,EAAE,CAAE,CAAC;YAC3B,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAAkB,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YACpE,MAAM,CAAC,IAAI,CAAC;gBACV,EAAE,EAAE,CAAC,CAAC,YAAY;gBAClB,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,QAAQ,EAAE,CAAC,CAAC,QAAQ;gBACpB,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,IAAI;aACL,CAAC,CAAC;YACH,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;QAClC,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,iFAAiF;IACjF,MAAM,CAAC,WAAmB,EAAE,YAAoB;QAC9C,OAAO,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;IACnD,CAAC;IAED,mEAAmE;IACnE,UAAU,CAAC,WAAmB;QAC5B,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;QAChC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IACxC,CAAC;IAED,iEAAiE;IACjE,EAAE;QACA,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QACvC,KAAK,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACtC,KAAK,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,GAAG,EAAE,CAAC;gBAC1B,IAAI,CAAC,CAAC,aAAa,IAAI,MAAM,EAAE,CAAC;oBAC9B,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;gBAC3B,CAAC;YACH,CAAC;YACD,IAAI,GAAG,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;gBACnB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;gBACzB,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACjC,CAAC;QACH,CAAC;IACH,CAAC;IAED,2DAA2D;IAC3D,IAAI,CAAC,WAAmB;QACtB,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,IAAI,IAAI,CAAC,CAAC;IACjD,CAAC;IAED,wDAAwD;IACxD,QAAQ,CAAC,WAAmB;QAC1B,OAAO,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IACjD,CAAC;IAED,2EAA2E;IAEnE,YAAY,CAAC,YAAoB,EAAE,IAAyD;QAClG,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAChD,MAAM,SAAS,CAAC,wBAAwB,EAAE,cAAc,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;QAC9E,CAAC;QACD,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACnF,MAAM,SAAS,CAAC,wBAAwB,EAAE,cAAc,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;QAC9E,CAAC;QACD,IAAI,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;YACtD,MAAM,SAAS,CAAC,sBAAsB,EAAE,GAAG,IAAI,CAAC,IAAI,YAAY,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,QAAQ,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;QACnI,CAAC;QACD,IAAI,OAAO,IAAI,CAAC,WAAW,KAAK,QAAQ,IAAI,IAAI,CAAC,WAAW,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;YACzG,MAAM,SAAS,CAAC,wBAAwB,EAAE,qBAAqB,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;QACrF,CAAC;QACD,IAAI,OAAO,IAAI,CAAC,QAAQ,KAAK,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACpE,MAAM,SAAS,CAAC,wBAAwB,EAAE,kBAAkB,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;QAClF,CAAC;QACD,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC5D,MAAM,SAAS,CAAC,wBAAwB,EAAE,cAAc,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;QAC9E,CAAC;IACH,CAAC;IAEO,OAAO,CAAC,CAAmB;QACjC,OAAO,CAAC,CAAC,aAAa,KAAK,CAAC,CAAC,WAAW,CAAC;IAC3C,CAAC;IAEO,SAAS,CAAC,WAAmB,EAAE,YAAoB;QACzD,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAC7C,IAAI,CAAC,OAAO;YAAE,OAAO,KAAK,CAAC;QAC3B,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QACpC,IAAI,CAAC,CAAC;YAAE,OAAO,KAAK,CAAC;QACrB,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QAC7B,MAAM,cAAc,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,aAAa,CAAC;QACnF,IAAI,cAAc,GAAG,CAAC,EAAE,CAAC;YACvB,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC;QACrD,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;QACxC,CAAC;QACD,IAAI,OAAO,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YACvB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;QAClC,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;CACF"}
@@ -0,0 +1,40 @@
1
+ import type { Attachment, AttachmentMetadata } from '@sumicom/quicksave-shared';
2
+ export interface PersistedMeta {
3
+ id: string;
4
+ kind: AttachmentMetadata['kind'];
5
+ mimeType: string;
6
+ name: string;
7
+ size: number;
8
+ storedAt: number;
9
+ }
10
+ /**
11
+ * Persist a batch of resolved attachments under one session id. Idempotent
12
+ * per id — re-writing the same id overwrites both files.
13
+ */
14
+ export declare function persistAttachments(sessionId: string, attachments: readonly Attachment[]): Promise<void>;
15
+ export interface TurnManifestEntry {
16
+ ids: string[];
17
+ }
18
+ /**
19
+ * Read the per-session attachment turn manifest in send order. Returns an
20
+ * empty array if no manifest exists yet (e.g. a session whose attachments
21
+ * predate this code), letting callers fall back to fuzzy rebind.
22
+ */
23
+ export declare function readTurnManifest(sessionId: string): Promise<TurnManifestEntry[]>;
24
+ /**
25
+ * Read one attachment's bytes back, returning the wire `Attachment` shape
26
+ * (metadata + base64 data). Returns `null` if the attachment isn't on disk
27
+ * — e.g. session was end-tasked, or the JSONL had an attachment we didn't
28
+ * persist (cold-start session before this code shipped).
29
+ */
30
+ export declare function loadAttachment(sessionId: string, attachmentId: string): Promise<Attachment | null>;
31
+ /** Drop every persisted attachment for one session (called on end-task). */
32
+ export declare function removeSessionAttachments(sessionId: string): Promise<void>;
33
+ /**
34
+ * List metadata for every attachment persisted under a session id. Mainly
35
+ * for diagnostics / tests.
36
+ */
37
+ export declare function listSessionAttachments(sessionId: string): Promise<PersistedMeta[]>;
38
+ /** True if the meta+bin pair exist on disk — without reading the bytes. */
39
+ export declare function hasPersistedAttachment(sessionId: string, attachmentId: string): Promise<boolean>;
40
+ //# sourceMappingURL=attachmentStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attachmentStore.d.ts","sourceRoot":"","sources":["../../src/ai/attachmentStore.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAGhF,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,kBAAkB,CAAC,MAAM,CAAC,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAkBD;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,SAAS,UAAU,EAAE,GACjC,OAAO,CAAC,IAAI,CAAC,CAqBf;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,EAAE,CAAC;CACf;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAoBtF;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAyB5B;AAED,4EAA4E;AAC5E,wBAAsB,wBAAwB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAM/E;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAkBxF;AAED,2EAA2E;AAC3E,wBAAsB,sBAAsB,CAC1C,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,OAAO,CAAC,CAUlB"}