@jini-ai/http-kit 0.2.1

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 (227) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +107 -0
  3. package/dist/active-context.d.ts +99 -0
  4. package/dist/active-context.d.ts.map +1 -0
  5. package/dist/active-context.js +77 -0
  6. package/dist/active-context.js.map +1 -0
  7. package/dist/adapter.d.ts +36 -0
  8. package/dist/adapter.d.ts.map +1 -0
  9. package/dist/adapter.js +92 -0
  10. package/dist/adapter.js.map +1 -0
  11. package/dist/agents.d.ts +55 -0
  12. package/dist/agents.d.ts.map +1 -0
  13. package/dist/agents.js +23 -0
  14. package/dist/agents.js.map +1 -0
  15. package/dist/api-security-middleware.d.ts +146 -0
  16. package/dist/api-security-middleware.d.ts.map +1 -0
  17. package/dist/api-security-middleware.js +276 -0
  18. package/dist/api-security-middleware.js.map +1 -0
  19. package/dist/attachments.d.ts +317 -0
  20. package/dist/attachments.d.ts.map +1 -0
  21. package/dist/attachments.js +664 -0
  22. package/dist/attachments.js.map +1 -0
  23. package/dist/cancel-owned-runs.d.ts +44 -0
  24. package/dist/cancel-owned-runs.d.ts.map +1 -0
  25. package/dist/cancel-owned-runs.js +35 -0
  26. package/dist/cancel-owned-runs.js.map +1 -0
  27. package/dist/compat.d.ts +22 -0
  28. package/dist/compat.d.ts.map +1 -0
  29. package/dist/compat.js +17 -0
  30. package/dist/compat.js.map +1 -0
  31. package/dist/component-catalog.d.ts +74 -0
  32. package/dist/component-catalog.d.ts.map +1 -0
  33. package/dist/component-catalog.js +74 -0
  34. package/dist/component-catalog.js.map +1 -0
  35. package/dist/connectors.d.ts +165 -0
  36. package/dist/connectors.d.ts.map +1 -0
  37. package/dist/connectors.js +474 -0
  38. package/dist/connectors.js.map +1 -0
  39. package/dist/daemon-status.d.ts +72 -0
  40. package/dist/daemon-status.d.ts.map +1 -0
  41. package/dist/daemon-status.js +60 -0
  42. package/dist/daemon-status.js.map +1 -0
  43. package/dist/db-ops.d.ts +116 -0
  44. package/dist/db-ops.d.ts.map +1 -0
  45. package/dist/db-ops.js +191 -0
  46. package/dist/db-ops.js.map +1 -0
  47. package/dist/delegated-tools.d.ts +46 -0
  48. package/dist/delegated-tools.d.ts.map +1 -0
  49. package/dist/delegated-tools.js +155 -0
  50. package/dist/delegated-tools.js.map +1 -0
  51. package/dist/express/run-stream.d.ts +15 -0
  52. package/dist/express/run-stream.d.ts.map +1 -0
  53. package/dist/express/run-stream.js +11 -0
  54. package/dist/express/run-stream.js.map +1 -0
  55. package/dist/express-index.d.ts +32 -0
  56. package/dist/express-index.d.ts.map +1 -0
  57. package/dist/express-index.js +14 -0
  58. package/dist/express-index.js.map +1 -0
  59. package/dist/fastify/adapter.d.ts +31 -0
  60. package/dist/fastify/adapter.d.ts.map +1 -0
  61. package/dist/fastify/adapter.js +64 -0
  62. package/dist/fastify/adapter.js.map +1 -0
  63. package/dist/fastify/agents.d.ts +13 -0
  64. package/dist/fastify/agents.d.ts.map +1 -0
  65. package/dist/fastify/agents.js +7 -0
  66. package/dist/fastify/agents.js.map +1 -0
  67. package/dist/fastify/api-security-middleware.d.ts +64 -0
  68. package/dist/fastify/api-security-middleware.d.ts.map +1 -0
  69. package/dist/fastify/api-security-middleware.js +139 -0
  70. package/dist/fastify/api-security-middleware.js.map +1 -0
  71. package/dist/fastify/compat.d.ts +22 -0
  72. package/dist/fastify/compat.d.ts.map +1 -0
  73. package/dist/fastify/compat.js +16 -0
  74. package/dist/fastify/compat.js.map +1 -0
  75. package/dist/fastify/daemon-status.d.ts +22 -0
  76. package/dist/fastify/daemon-status.d.ts.map +1 -0
  77. package/dist/fastify/daemon-status.js +9 -0
  78. package/dist/fastify/daemon-status.js.map +1 -0
  79. package/dist/fastify/host-tools.d.ts +13 -0
  80. package/dist/fastify/host-tools.d.ts.map +1 -0
  81. package/dist/fastify/host-tools.js +8 -0
  82. package/dist/fastify/host-tools.js.map +1 -0
  83. package/dist/fastify/index.d.ts +36 -0
  84. package/dist/fastify/index.d.ts.map +1 -0
  85. package/dist/fastify/index.js +18 -0
  86. package/dist/fastify/index.js.map +1 -0
  87. package/dist/fastify/local-daemon-request.d.ts +43 -0
  88. package/dist/fastify/local-daemon-request.d.ts.map +1 -0
  89. package/dist/fastify/local-daemon-request.js +155 -0
  90. package/dist/fastify/local-daemon-request.js.map +1 -0
  91. package/dist/fastify/origin.d.ts +21 -0
  92. package/dist/fastify/origin.d.ts.map +1 -0
  93. package/dist/fastify/origin.js +14 -0
  94. package/dist/fastify/origin.js.map +1 -0
  95. package/dist/fastify/request.d.ts +20 -0
  96. package/dist/fastify/request.d.ts.map +1 -0
  97. package/dist/fastify/request.js +25 -0
  98. package/dist/fastify/request.js.map +1 -0
  99. package/dist/fastify/response.d.ts +20 -0
  100. package/dist/fastify/response.d.ts.map +1 -0
  101. package/dist/fastify/response.js +41 -0
  102. package/dist/fastify/response.js.map +1 -0
  103. package/dist/fastify/route-registration-guard.d.ts +70 -0
  104. package/dist/fastify/route-registration-guard.d.ts.map +1 -0
  105. package/dist/fastify/route-registration-guard.js +69 -0
  106. package/dist/fastify/route-registration-guard.js.map +1 -0
  107. package/dist/fastify/run-stream.d.ts +18 -0
  108. package/dist/fastify/run-stream.d.ts.map +1 -0
  109. package/dist/fastify/run-stream.js +10 -0
  110. package/dist/fastify/run-stream.js.map +1 -0
  111. package/dist/fastify/runs.d.ts +17 -0
  112. package/dist/fastify/runs.d.ts.map +1 -0
  113. package/dist/fastify/runs.js +33 -0
  114. package/dist/fastify/runs.js.map +1 -0
  115. package/dist/frontend-control.d.ts +131 -0
  116. package/dist/frontend-control.d.ts.map +1 -0
  117. package/dist/frontend-control.js +77 -0
  118. package/dist/frontend-control.js.map +1 -0
  119. package/dist/frontend-sessions.d.ts +100 -0
  120. package/dist/frontend-sessions.d.ts.map +1 -0
  121. package/dist/frontend-sessions.js +180 -0
  122. package/dist/frontend-sessions.js.map +1 -0
  123. package/dist/health.d.ts +91 -0
  124. package/dist/health.d.ts.map +1 -0
  125. package/dist/health.js +53 -0
  126. package/dist/health.js.map +1 -0
  127. package/dist/host-tools.d.ts +174 -0
  128. package/dist/host-tools.d.ts.map +1 -0
  129. package/dist/host-tools.js +338 -0
  130. package/dist/host-tools.js.map +1 -0
  131. package/dist/index.d.ts +86 -0
  132. package/dist/index.d.ts.map +1 -0
  133. package/dist/index.js +40 -0
  134. package/dist/index.js.map +1 -0
  135. package/dist/local-daemon-request.d.ts +44 -0
  136. package/dist/local-daemon-request.d.ts.map +1 -0
  137. package/dist/local-daemon-request.js +145 -0
  138. package/dist/local-daemon-request.js.map +1 -0
  139. package/dist/media.d.ts +131 -0
  140. package/dist/media.d.ts.map +1 -0
  141. package/dist/media.js +352 -0
  142. package/dist/media.js.map +1 -0
  143. package/dist/memory.d.ts +230 -0
  144. package/dist/memory.d.ts.map +1 -0
  145. package/dist/memory.js +331 -0
  146. package/dist/memory.js.map +1 -0
  147. package/dist/model-proxy.d.ts +45 -0
  148. package/dist/model-proxy.d.ts.map +1 -0
  149. package/dist/model-proxy.js +619 -0
  150. package/dist/model-proxy.js.map +1 -0
  151. package/dist/origin-validation.d.ts +64 -0
  152. package/dist/origin-validation.d.ts.map +1 -0
  153. package/dist/origin-validation.js +226 -0
  154. package/dist/origin-validation.js.map +1 -0
  155. package/dist/origin.d.ts +32 -0
  156. package/dist/origin.d.ts.map +1 -0
  157. package/dist/origin.js +14 -0
  158. package/dist/origin.js.map +1 -0
  159. package/dist/pack-http.d.ts +30 -0
  160. package/dist/pack-http.d.ts.map +1 -0
  161. package/dist/pack-http.js +12 -0
  162. package/dist/pack-http.js.map +1 -0
  163. package/dist/raw-sse.d.ts +44 -0
  164. package/dist/raw-sse.d.ts.map +1 -0
  165. package/dist/raw-sse.js +93 -0
  166. package/dist/raw-sse.js.map +1 -0
  167. package/dist/remote-run-events.d.ts +66 -0
  168. package/dist/remote-run-events.d.ts.map +1 -0
  169. package/dist/remote-run-events.js +230 -0
  170. package/dist/remote-run-events.js.map +1 -0
  171. package/dist/request.d.ts +19 -0
  172. package/dist/request.d.ts.map +1 -0
  173. package/dist/request.js +25 -0
  174. package/dist/request.js.map +1 -0
  175. package/dist/research.d.ts +42 -0
  176. package/dist/research.d.ts.map +1 -0
  177. package/dist/research.js +316 -0
  178. package/dist/research.js.map +1 -0
  179. package/dist/response.d.ts +14 -0
  180. package/dist/response.d.ts.map +1 -0
  181. package/dist/response.js +45 -0
  182. package/dist/response.js.map +1 -0
  183. package/dist/route-manifest.d.ts +31 -0
  184. package/dist/route-manifest.d.ts.map +1 -0
  185. package/dist/route-manifest.js +103 -0
  186. package/dist/route-manifest.js.map +1 -0
  187. package/dist/route-registration-guard.d.ts +65 -0
  188. package/dist/route-registration-guard.d.ts.map +1 -0
  189. package/dist/route-registration-guard.js +71 -0
  190. package/dist/route-registration-guard.js.map +1 -0
  191. package/dist/routines.d.ts +114 -0
  192. package/dist/routines.d.ts.map +1 -0
  193. package/dist/routines.js +320 -0
  194. package/dist/routines.js.map +1 -0
  195. package/dist/run-stream.d.ts +60 -0
  196. package/dist/run-stream.d.ts.map +1 -0
  197. package/dist/run-stream.js +108 -0
  198. package/dist/run-stream.js.map +1 -0
  199. package/dist/runs.d.ts +79 -0
  200. package/dist/runs.d.ts.map +1 -0
  201. package/dist/runs.js +232 -0
  202. package/dist/runs.js.map +1 -0
  203. package/dist/sse.d.ts +139 -0
  204. package/dist/sse.d.ts.map +1 -0
  205. package/dist/sse.js +157 -0
  206. package/dist/sse.js.map +1 -0
  207. package/dist/terminals.d.ts +125 -0
  208. package/dist/terminals.d.ts.map +1 -0
  209. package/dist/terminals.js +349 -0
  210. package/dist/terminals.js.map +1 -0
  211. package/dist/tool-catalog.d.ts +68 -0
  212. package/dist/tool-catalog.d.ts.map +1 -0
  213. package/dist/tool-catalog.js +68 -0
  214. package/dist/tool-catalog.js.map +1 -0
  215. package/dist/types.d.ts +58 -0
  216. package/dist/types.d.ts.map +1 -0
  217. package/dist/types.js +5 -0
  218. package/dist/types.js.map +1 -0
  219. package/dist/workspace-root.d.ts +69 -0
  220. package/dist/workspace-root.d.ts.map +1 -0
  221. package/dist/workspace-root.js +63 -0
  222. package/dist/workspace-root.js.map +1 -0
  223. package/dist/xai.d.ts +86 -0
  224. package/dist/xai.d.ts.map +1 -0
  225. package/dist/xai.js +668 -0
  226. package/dist/xai.js.map +1 -0
  227. package/package.json +58 -0
@@ -0,0 +1,317 @@
1
+ import type { Express, Request, Response } from 'express';
2
+ import type { AdapterContext } from './adapter.js';
3
+ /**
4
+ * A staged upload as it crosses the wire and as `claim()` returns it.
5
+ *
6
+ * Deliberately declared here rather than imported from `@jini-ai/chat/core`, whose `ChatAttachment`
7
+ * this mirrors field-for-field: the same reason `media.ts` declares its own `MediaTask`/
8
+ * `MediaDispatchEngine` port types instead of depending on `@jini-ai/media`. A transport package
9
+ * should not acquire a dependency on a *domain* package (`jini.domain: "chat"`) to describe an
10
+ * upload that has nothing chat-specific about it. `attachments.test.ts` holds a compile-time
11
+ * assignability check in both directions against the real `ChatAttachment`, so the mirror cannot
12
+ * silently drift — the drift risk is paid for with a test rather than with a dependency edge.
13
+ *
14
+ * `path` means two different things at two different times, on purpose:
15
+ * - as returned by `register()` / over the wire: an opaque `attachment:<uuid>` capability id;
16
+ * - as returned by `claim()`: the real absolute filesystem path, server-side only.
17
+ */
18
+ export interface StoredAttachment {
19
+ path: string;
20
+ name: string;
21
+ kind: 'image' | 'file';
22
+ size?: number;
23
+ /**
24
+ * User-visible ordering for the turn that carries this attachment. Present for exact structural
25
+ * parity with `ChatAttachment`; this store never sets it, because display ordering is the
26
+ * renderer's concern and nothing here would be able to reconstruct it.
27
+ */
28
+ order?: number;
29
+ }
30
+ /** What `claim()` hands back: trusted paths plus the one directory an agent may be granted. */
31
+ export interface AttachmentClaim {
32
+ attachments: StoredAttachment[];
33
+ /** Absent only for an empty claim — there is no directory to grant when nothing was claimed. */
34
+ batchDirectory?: string;
35
+ }
36
+ /** Why an attachment operation was refused. Maps to an HTTP status in `statusForRejection`. */
37
+ export type AttachmentRejectionReason =
38
+ /** The batch id is missing or not of the accepted shape. */
39
+ 'invalid-batch'
40
+ /** One file exceeded the per-attachment byte cap. */
41
+ | 'attachment-too-large'
42
+ /** The batch already holds `maxAttachments` files. */
43
+ | 'batch-count-exceeded'
44
+ /** This file would push the batch past `maxBatchBytes`. */
45
+ | 'batch-too-large'
46
+ /** The store as a whole is at `maxStoredAttachments` / `maxStoredBytes`. */
47
+ | 'storage-full'
48
+ /** Nothing was uploaded — a zero-byte body. */
49
+ | 'empty-attachment'
50
+ /** More concurrent uploads than `maxConcurrentUploads`. */
51
+ | 'too-many-concurrent-uploads'
52
+ /** The cleanup request body was not a `{ batchId, paths }` of the accepted shape. */
53
+ | 'invalid-cleanup-request'
54
+ /**
55
+ * The request stream was already drained before this route saw it — a host body-parser ordering
56
+ * problem, not anything the caller did. See this module's doc.
57
+ */
58
+ | 'attachment-body-consumed'
59
+ /**
60
+ * The stored file is not the canonical regular file it was registered as. Reported opaquely over
61
+ * HTTP: this means either a bug or an active attempt to redirect a claim.
62
+ */
63
+ | 'attachment-integrity'
64
+ /** More attachments in one claim than `maxAttachments` allows. */
65
+ | 'too-many-attachments'
66
+ /** The same capability id appeared twice in one claim. */
67
+ | 'duplicate-attachment'
68
+ /** No such capability id, or a run already claimed it. */
69
+ | 'attachment-unknown-or-claimed'
70
+ /** One claim spanned more than one batch. */
71
+ | 'mixed-batch';
72
+ /**
73
+ * A refusal this module classified itself, as opposed to an unexpected filesystem/programming
74
+ * error. The route pack turns the `reason` into a status code and lets the `message` through to the
75
+ * caller; anything that is *not* one of these is redacted to a generic `INTERNAL_ERROR`.
76
+ */
77
+ export declare class AttachmentRejectedError extends Error {
78
+ readonly reason: AttachmentRejectionReason;
79
+ constructor(reason: AttachmentRejectionReason, message: string);
80
+ }
81
+ /**
82
+ * Registers, validates, and expires uploads staged for a run. `createDiskAttachmentStore` is the
83
+ * implementation this package ships; a host with its own storage (object store, tmpfs, a quota
84
+ * system of its own) can satisfy this port instead and keep the route pack.
85
+ */
86
+ export interface AttachmentStore {
87
+ /** Creates (idempotently) the private directory that holds one batch's files. */
88
+ createBatchDirectory: (batchId: string) => Promise<string>;
89
+ /**
90
+ * Takes ownership of an already-written file and returns its opaque capability record. Rejects —
91
+ * and deletes the file — if it is not a canonical regular file directly inside its batch
92
+ * directory, or if any quota would be exceeded.
93
+ */
94
+ register: (input: {
95
+ batchId: string;
96
+ path: string;
97
+ name: string;
98
+ kind: StoredAttachment['kind'];
99
+ size: number;
100
+ }) => Promise<StoredAttachment>;
101
+ /** Exchanges capability ids for real paths, exactly once, binding them to `runId`. */
102
+ claim: (attachments: readonly StoredAttachment[], runId: string) => Promise<AttachmentClaim>;
103
+ /** Deletes the named still-unclaimed uploads, then the batch directory if it is now empty. */
104
+ deleteUnclaimed: (batchId: string, paths: readonly string[]) => Promise<void>;
105
+ /** Deletes everything `runId` claimed. Safe to call for a run that claimed nothing. */
106
+ cleanupRun: (runId: string) => Promise<void>;
107
+ /** Deletes unclaimed uploads older than the retention window. */
108
+ pruneExpired: (now?: number) => Promise<void>;
109
+ /** Deletes every tracked upload. For host shutdown. */
110
+ dispose: () => Promise<void>;
111
+ }
112
+ export interface CreateDiskAttachmentStoreOptions {
113
+ /** Root directory this store owns outright — it is emptied on construction. */
114
+ readonly uploadDirectory: string;
115
+ /** Files per batch, i.e. per composer turn. Also caps one `claim()`. Defaults to 10. */
116
+ readonly maxAttachments?: number;
117
+ /** Total bytes per batch. Defaults to 50 MB. */
118
+ readonly maxBatchBytes?: number;
119
+ /** Tracked files across all batches. Defaults to 100. */
120
+ readonly maxStoredAttachments?: number;
121
+ /** Tracked bytes across all batches. Defaults to 200 MB. */
122
+ readonly maxStoredBytes?: number;
123
+ /** How long an unclaimed upload survives `pruneExpired`. Defaults to one hour. */
124
+ readonly retentionMs?: number;
125
+ }
126
+ export interface AttachmentRecord {
127
+ id: string;
128
+ filePath: string;
129
+ name: string;
130
+ kind: StoredAttachment['kind'];
131
+ size: number;
132
+ batchId: string;
133
+ batchDirectory: string;
134
+ dev: number;
135
+ ino: number;
136
+ createdAt: number;
137
+ claimedRunId?: string;
138
+ }
139
+ /** What registration recorded about a file, as `isUnchangedAttachment` needs it. */
140
+ export interface RecordedAttachmentIdentity {
141
+ readonly filePath: string;
142
+ readonly dev: number;
143
+ readonly ino: number;
144
+ readonly size: number;
145
+ }
146
+ /**
147
+ * What the filesystem reports about a file right now — the subset of `fs.Stats` (plus `realpath`)
148
+ * an integrity check needs.
149
+ *
150
+ * `isRegularFile` comes from `lstat().isFile()`, which is false for a symlink *and* for a
151
+ * directory. There is deliberately no separate `isSymbolicLink` field: `lstat` reports exactly one
152
+ * file type, so `isFile()` and `isSymbolicLink()` are mutually exclusive and a symlink check after
153
+ * an `isFile()` check could never be the deciding one.
154
+ */
155
+ export interface ObservedAttachmentIdentity {
156
+ readonly isRegularFile: boolean;
157
+ readonly dev: number;
158
+ readonly ino: number;
159
+ readonly size: number;
160
+ /** `realpath` of the file — differs from its own path when any path component became a symlink. */
161
+ readonly canonicalPath: string;
162
+ }
163
+ /**
164
+ * `true` when the file on disk is still the same file registration accepted.
165
+ *
166
+ * Every condition is a distinct real attack: a regular file swapped for a symlink or directory, a
167
+ * parent directory swapped for a symlink pointing elsewhere, the file replaced by a different file
168
+ * at the same path (new inode), the same inode truncated or appended to (new size), or the path
169
+ * now resolving onto a different device.
170
+ *
171
+ * Pure and exported on purpose. Staging a *device* change for a file that keeps its path is not
172
+ * something a test can do on a real filesystem, and a check that can only be exercised in
173
+ * production is a check nobody knows works — a fake identity states each case directly.
174
+ */
175
+ export declare function isUnchangedAttachment(recorded: RecordedAttachmentIdentity, observed: ObservedAttachmentIdentity): boolean;
176
+ /**
177
+ * Phase 1 of `claim()`: synchronously reserves `attachments` for `runId`, or throws (releasing
178
+ * whatever it already reserved this call) the first time a requested attachment turns out to be
179
+ * unknown or already claimed.
180
+ *
181
+ * The reservation loop deliberately has no `await`: this store's exactly-once guarantee is what
182
+ * stops two runs being handed the same real path on disk, and `claim` is reachable concurrently
183
+ * (two run starts, one shared attachment). Nothing between `records.get` and the assignment of
184
+ * `record.claimedRunId` may ever become asynchronous — that window is exactly where a concurrent
185
+ * call would get its turn and could observe the same record as still unclaimed.
186
+ *
187
+ * Exported so this invariant can be exercised directly against a plain `Map` of fabricated
188
+ * records, without going through a disk-backed store.
189
+ */
190
+ export declare function reserveAttachmentRecords(attachments: readonly StoredAttachment[], records: ReadonlyMap<string, AttachmentRecord>, runId: string, maxAttachments: number): AttachmentRecord[];
191
+ /**
192
+ * Phase 2 of `claim()`: re-verifies every already-reserved record against the filesystem — not
193
+ * merely re-read, but re-checked against what registration recorded, so someone able to write into
194
+ * the batch directory between upload and run start cannot get the agent to read a file of their
195
+ * choosing — and checks every claimed attachment shares one batch. Returns the shared batch
196
+ * directory on success (or `''` for an empty `claimed`, which `claim()` never passes in — it
197
+ * returns before calling this — but which a direct caller can still treat as "no batch").
198
+ *
199
+ * Throws without releasing `claimed`'s reservations; releasing is the caller's job (`claim`'s own
200
+ * `catch`), so a rejected claim still leaves nothing half-claimed and the caller can retry with a
201
+ * corrected set.
202
+ *
203
+ * Exported so the integrity and mixed-batch checks can be exercised directly against fabricated
204
+ * records over real files on disk, without a full `createDiskAttachmentStore` around them.
205
+ */
206
+ export declare function verifyClaimedAttachments(claimed: readonly AttachmentRecord[]): Promise<string>;
207
+ /**
208
+ * Reduces an untrusted upload filename to a display-only basename over a conservative allowlist.
209
+ * Never used as the name of the stored file — see this module's trust-model doc.
210
+ */
211
+ export declare function sanitizeAttachmentName(requestedName: unknown): string;
212
+ /** `true` when `body`'s first 8 bytes are the PNG signature. */
213
+ export declare function hasPngSignature(body: Uint8Array): boolean;
214
+ /** `true` when `body`'s first 3 bytes are the JPEG start-of-image marker. */
215
+ export declare function hasJpegSignature(body: Uint8Array): boolean;
216
+ /** `true` when `body`'s first 6 bytes spell either GIF version tag. */
217
+ export declare function hasGifSignature(body: Uint8Array): boolean;
218
+ /** `true` when `body` opens with a RIFF container whose form type is WEBP. */
219
+ export declare function hasWebpSignature(body: Uint8Array): boolean;
220
+ /**
221
+ * Infers `'image'` from the leading bytes rather than from a renderer-controlled MIME type or file
222
+ * extension. PNG, JPEG, GIF87a/89a, and WEBP are recognized; everything else is `'file'`.
223
+ *
224
+ * `kind` decides whether a path is later passed to `AgentExecutor.run()`'s `imagePaths`, so letting
225
+ * a renderer assert it would let a renderer choose how the agent runtime parses the bytes.
226
+ */
227
+ export declare function detectAttachmentKind(body: Uint8Array): StoredAttachment['kind'];
228
+ /**
229
+ * Streams a request body straight to a private file under a hard byte cap, keeping only the
230
+ * leading signature bytes in memory so an upload never costs memory proportional to its size.
231
+ *
232
+ * Opened `wx`, so this can never overwrite an existing file. A partial write is removed before the
233
+ * rejection propagates: the cap is enforced *during* the stream, which necessarily means some bytes
234
+ * already reached disk by the time it trips.
235
+ */
236
+ export declare function writeBoundedAttachmentBody({ request, filePath, maxBytes, mode, }: {
237
+ request: AsyncIterable<unknown>;
238
+ filePath: string;
239
+ maxBytes: number;
240
+ mode?: number;
241
+ }): Promise<{
242
+ size: number;
243
+ signature: Uint8Array;
244
+ }>;
245
+ /**
246
+ * The disk-backed `AttachmentStore` this package ships. Every default matches what a chat composer
247
+ * needs out of the box; a host that wants different quotas passes them rather than reimplementing
248
+ * the port.
249
+ *
250
+ * @complexity `register`/`claim` are O(n) in the number of tracked records (a small bounded number
251
+ * — `maxStoredAttachments`), which is what keeps the quota decision synchronous; see `register`.
252
+ */
253
+ export declare function createDiskAttachmentStore({ uploadDirectory, maxAttachments, maxBatchBytes, maxStoredAttachments, maxStoredBytes, retentionMs, }: CreateDiskAttachmentStoreOptions): Promise<AttachmentStore>;
254
+ /**
255
+ * Diagnostic detail for an internal-error response the public API deliberately does not disclose
256
+ * (SEC-005), matching `MediaInternalErrorContext`'s precedent.
257
+ */
258
+ export interface AttachmentsInternalErrorContext {
259
+ readonly source: 'attachment-upload' | 'attachment-cleanup';
260
+ readonly batchId: string | null;
261
+ readonly correlationId: string;
262
+ readonly error: unknown;
263
+ }
264
+ export interface AttachmentsHttpDeps {
265
+ readonly store: AttachmentStore;
266
+ /**
267
+ * Uploads accepted at once before further requests are rate-limited. Bounds concurrent
268
+ * filesystem work, which matters because each in-flight upload holds an open handle and may be
269
+ * writing up to `maxAttachmentBytes`. Defaults to 4.
270
+ */
271
+ readonly maxConcurrentUploads?: number;
272
+ /**
273
+ * Hard per-request byte cap, enforced while streaming. Should be at or below the store's
274
+ * `maxBatchBytes`. Defaults to 20 MB.
275
+ */
276
+ readonly maxAttachmentBytes?: number;
277
+ /** Capability ids one `DELETE` may name. Defaults to 10, matching the store's batch cap. */
278
+ readonly maxCleanupPaths?: number;
279
+ /**
280
+ * Rejects a request whose `Origin`/`Host` is not the local daemon's, as every other mutating
281
+ * route in this package does. Defaults to `true`; a host whose browser sits on a *different*
282
+ * local port than the daemon (a dev-server proxy, say) either sets that port via `JINI_WEB_PORT`
283
+ * so the guard recognizes it, or opts out here and accepts that it has no CSRF protection on
284
+ * this route.
285
+ */
286
+ readonly requireSameOrigin?: boolean;
287
+ /** Host-owned sink for the real exception behind a generic `INTERNAL_ERROR` (SEC-005). Defaults to `console.error`. */
288
+ readonly onInternalError?: (context: AttachmentsInternalErrorContext) => void;
289
+ }
290
+ export declare const ATTACHMENTS_ROUTE_PATH = "/api/attachments";
291
+ export interface AttachmentUploadResponse {
292
+ readonly attachment: StoredAttachment;
293
+ }
294
+ /**
295
+ * Handles one upload: rate-limit, batch directory, bounded stream to disk, signature sniff,
296
+ * register. Exported so a host mounting its own path (or its own framework) can reuse the whole
297
+ * body without re-deriving the ordering, which is load-bearing — see the `finally`.
298
+ */
299
+ export declare function handleAttachmentUpload(req: Request, res: Response, deps: AttachmentsHttpDeps, state: {
300
+ activeUploads: number;
301
+ }): Promise<void>;
302
+ /**
303
+ * Handles a client abandoning a batch (a failed multi-file upload, a cleared composer). Only
304
+ * *unclaimed* uploads can be deleted this way, so a caller cannot use it to pull files out from
305
+ * under a run that already claimed them.
306
+ */
307
+ export declare function handleAttachmentCleanup(req: Request, res: Response, deps: AttachmentsHttpDeps): Promise<void>;
308
+ /**
309
+ * Mounts `POST`/`DELETE /api/attachments` on `app`. A pack's `http(app, services)` calls this
310
+ * directly.
311
+ *
312
+ * Hand-mounted rather than built from `defineJsonRoute`/`mountJsonRoute` for two concrete reasons:
313
+ * the upload reads the raw request stream (a JSON-parsed `req.body` is exactly what must not have
314
+ * happened), and the cleanup answers `204` with no body, which a JSON responder cannot express.
315
+ */
316
+ export declare function registerAttachmentRoutes(app: Express, deps: AttachmentsHttpDeps, adapter: AdapterContext): void;
317
+ //# sourceMappingURL=attachments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attachments.d.ts","sourceRoot":"","sources":["../src/attachments.ts"],"names":[],"mappings":"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"}