@sumicom/quicksave 0.8.6 → 0.8.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +1 -1
- package/dist/ai/asyncQueue.d.ts.map +1 -1
- package/dist/ai/asyncQueue.js +2 -0
- package/dist/ai/asyncQueue.js.map +1 -1
- package/dist/ai/attachmentStaging.d.ts +71 -0
- package/dist/ai/attachmentStaging.d.ts.map +1 -0
- package/dist/ai/attachmentStaging.js +243 -0
- package/dist/ai/attachmentStaging.js.map +1 -0
- package/dist/ai/attachmentStore.d.ts +40 -0
- package/dist/ai/attachmentStore.d.ts.map +1 -0
- package/dist/ai/attachmentStore.js +173 -0
- package/dist/ai/attachmentStore.js.map +1 -0
- package/dist/ai/cardBuilder.d.ts +3 -2
- package/dist/ai/cardBuilder.d.ts.map +1 -1
- package/dist/ai/cardBuilder.js +156 -5
- package/dist/ai/cardBuilder.js.map +1 -1
- package/dist/ai/claudeCliProvider.d.ts +7 -2
- package/dist/ai/claudeCliProvider.d.ts.map +1 -1
- package/dist/ai/claudeCliProvider.js +29 -11
- package/dist/ai/claudeCliProvider.js.map +1 -1
- package/dist/ai/claudeCodeProvider.d.ts.map +1 -1
- package/dist/ai/claudeCodeProvider.js +2 -0
- package/dist/ai/claudeCodeProvider.js.map +1 -1
- package/dist/ai/claudeSdkProvider.d.ts +2 -1
- package/dist/ai/claudeSdkProvider.d.ts.map +1 -1
- package/dist/ai/claudeSdkProvider.js +76 -12
- package/dist/ai/claudeSdkProvider.js.map +1 -1
- package/dist/ai/codexAppServer/approvalMapping.d.ts.map +1 -1
- package/dist/ai/codexAppServer/approvalMapping.js.map +1 -1
- package/dist/ai/codexAppServer/cardAdapter.d.ts.map +1 -1
- package/dist/ai/codexAppServer/cardAdapter.js.map +1 -1
- package/dist/ai/codexAppServer/index.d.ts.map +1 -1
- package/dist/ai/codexAppServer/index.js +2 -0
- package/dist/ai/codexAppServer/index.js.map +1 -1
- package/dist/ai/codexAppServer/overrideStore.d.ts.map +1 -1
- package/dist/ai/codexAppServer/overrideStore.js.map +1 -1
- package/dist/ai/codexAppServer/permissionMapping.d.ts.map +1 -1
- package/dist/ai/codexAppServer/permissionMapping.js.map +1 -1
- package/dist/ai/codexAppServer/processManager.d.ts.map +1 -1
- package/dist/ai/codexAppServer/processManager.js +2 -0
- package/dist/ai/codexAppServer/processManager.js.map +1 -1
- package/dist/ai/codexAppServer/provider.d.ts.map +1 -1
- package/dist/ai/codexAppServer/provider.js +1 -1
- package/dist/ai/codexAppServer/provider.js.map +1 -1
- package/dist/ai/codexAppServer/rpcClient.d.ts.map +1 -1
- package/dist/ai/codexAppServer/rpcClient.js.map +1 -1
- package/dist/ai/codexAppServer/schema/index.d.ts.map +1 -1
- package/dist/ai/codexAppServer/schema/index.js +2 -0
- package/dist/ai/codexAppServer/schema/index.js.map +1 -1
- package/dist/ai/codexAppServer/stdioTransport.d.ts.map +1 -1
- package/dist/ai/codexAppServer/stdioTransport.js.map +1 -1
- package/dist/ai/codexAppServer/tokenAccounting.d.ts.map +1 -1
- package/dist/ai/codexAppServer/tokenAccounting.js.map +1 -1
- package/dist/ai/codexAppServer/version.d.ts.map +1 -1
- package/dist/ai/codexAppServer/version.js +2 -0
- package/dist/ai/codexAppServer/version.js.map +1 -1
- package/dist/ai/codexLogin.d.ts.map +1 -1
- package/dist/ai/codexLogin.js +2 -0
- package/dist/ai/codexLogin.js.map +1 -1
- package/dist/ai/codexMcpProvider.d.ts.map +1 -1
- package/dist/ai/codexMcpProvider.js +2 -0
- package/dist/ai/codexMcpProvider.js.map +1 -1
- package/dist/ai/commitSummary.d.ts.map +1 -1
- package/dist/ai/commitSummary.js +2 -0
- package/dist/ai/commitSummary.js.map +1 -1
- package/dist/ai/commitSummaryCli.d.ts.map +1 -1
- package/dist/ai/commitSummaryCli.js +2 -0
- package/dist/ai/commitSummaryCli.js.map +1 -1
- package/dist/ai/commitSummaryStore.d.ts.map +1 -1
- package/dist/ai/commitSummaryStore.js +2 -0
- package/dist/ai/commitSummaryStore.js.map +1 -1
- package/dist/ai/contentBlocks.d.ts +9 -0
- package/dist/ai/contentBlocks.d.ts.map +1 -0
- package/dist/ai/contentBlocks.js +86 -0
- package/dist/ai/contentBlocks.js.map +1 -0
- package/dist/ai/debugLogger.d.ts.map +1 -1
- package/dist/ai/debugLogger.js +2 -0
- package/dist/ai/debugLogger.js.map +1 -1
- package/dist/ai/enrichEntry.d.ts.map +1 -1
- package/dist/ai/enrichEntry.js.map +1 -1
- package/dist/ai/pdfMeta.d.ts +8 -0
- package/dist/ai/pdfMeta.d.ts.map +1 -0
- package/dist/ai/pdfMeta.js +51 -0
- package/dist/ai/pdfMeta.js.map +1 -0
- package/dist/ai/provider.d.ts +14 -6
- package/dist/ai/provider.d.ts.map +1 -1
- package/dist/ai/provider.js.map +1 -1
- package/dist/ai/sandboxMcp.d.ts.map +1 -1
- package/dist/ai/sandboxMcp.js +2 -0
- package/dist/ai/sandboxMcp.js.map +1 -1
- package/dist/ai/sandboxMcpStdio.d.ts.map +1 -1
- package/dist/ai/sandboxMcpStdio.js +2 -0
- package/dist/ai/sandboxMcpStdio.js.map +1 -1
- package/dist/ai/sessionManager.d.ts +12 -2
- package/dist/ai/sessionManager.d.ts.map +1 -1
- package/dist/ai/sessionManager.js +39 -5
- package/dist/ai/sessionManager.js.map +1 -1
- package/dist/ai/sessionRegistry.d.ts.map +1 -1
- package/dist/ai/sessionRegistry.js +2 -0
- package/dist/ai/sessionRegistry.js.map +1 -1
- package/dist/ai/sessionStore.d.ts.map +1 -1
- package/dist/ai/sessionStore.js +2 -0
- package/dist/ai/sessionStore.js.map +1 -1
- package/dist/ai/systemPrompt.d.ts.map +1 -1
- package/dist/ai/systemPrompt.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +2 -0
- package/dist/config.js.map +1 -1
- package/dist/connection/connection.d.ts.map +1 -1
- package/dist/connection/connection.js +2 -0
- package/dist/connection/connection.js.map +1 -1
- package/dist/connection/e2eHarness.d.ts.map +1 -1
- package/dist/connection/e2eHarness.js +2 -0
- package/dist/connection/e2eHarness.js.map +1 -1
- package/dist/connection/fakeRelay.d.ts.map +1 -1
- package/dist/connection/fakeRelay.js +2 -0
- package/dist/connection/fakeRelay.js.map +1 -1
- package/dist/connection/pubsub.d.ts.map +1 -1
- package/dist/connection/pubsub.js +2 -0
- package/dist/connection/pubsub.js.map +1 -1
- package/dist/connection/relay.d.ts.map +1 -1
- package/dist/connection/relay.js +2 -0
- package/dist/connection/relay.js.map +1 -1
- package/dist/files/fileBrowser.d.ts.map +1 -1
- package/dist/files/fileBrowser.js +2 -0
- package/dist/files/fileBrowser.js.map +1 -1
- package/dist/git/operations.d.ts.map +1 -1
- package/dist/git/operations.js +2 -0
- package/dist/git/operations.js.map +1 -1
- package/dist/handlers/legacyBusAdapter.d.ts +1 -1
- package/dist/handlers/legacyBusAdapter.d.ts.map +1 -1
- package/dist/handlers/legacyBusAdapter.js +13 -1
- package/dist/handlers/legacyBusAdapter.js.map +1 -1
- package/dist/handlers/messageHandler.d.ts +23 -0
- package/dist/handlers/messageHandler.d.ts.map +1 -1
- package/dist/handlers/messageHandler.js +223 -7
- package/dist/handlers/messageHandler.js.map +1 -1
- package/dist/index.js +42 -2
- package/dist/index.js.map +1 -1
- package/dist/messageBus/busServerTransport.d.ts.map +1 -1
- package/dist/messageBus/busServerTransport.js +2 -0
- package/dist/messageBus/busServerTransport.js.map +1 -1
- package/dist/service/debugHttpServer.d.ts.map +1 -1
- package/dist/service/debugHttpServer.js +2 -0
- package/dist/service/debugHttpServer.js.map +1 -1
- package/dist/service/ensureDaemon.d.ts.map +1 -1
- package/dist/service/ensureDaemon.js +27 -2
- package/dist/service/ensureDaemon.js.map +1 -1
- package/dist/service/index.d.ts.map +1 -1
- package/dist/service/index.js +2 -0
- package/dist/service/index.js.map +1 -1
- package/dist/service/ipcClient.d.ts.map +1 -1
- package/dist/service/ipcClient.js +3 -1
- package/dist/service/ipcClient.js.map +1 -1
- package/dist/service/ipcServer.d.ts.map +1 -1
- package/dist/service/ipcServer.js +2 -0
- package/dist/service/ipcServer.js.map +1 -1
- package/dist/service/pushClient.d.ts.map +1 -1
- package/dist/service/pushClient.js +2 -0
- package/dist/service/pushClient.js.map +1 -1
- package/dist/service/run.d.ts.map +1 -1
- package/dist/service/run.js +14 -1
- package/dist/service/run.js.map +1 -1
- package/dist/service/singleton.d.ts +1 -0
- package/dist/service/singleton.d.ts.map +1 -1
- package/dist/service/singleton.js +5 -0
- package/dist/service/singleton.js.map +1 -1
- package/dist/service/stateStore.d.ts.map +1 -1
- package/dist/service/stateStore.js +2 -0
- package/dist/service/stateStore.js.map +1 -1
- package/dist/service/systemdUnit.d.ts +149 -0
- package/dist/service/systemdUnit.d.ts.map +1 -0
- package/dist/service/systemdUnit.js +322 -0
- package/dist/service/systemdUnit.js.map +1 -0
- package/dist/service/types.d.ts +6 -0
- package/dist/service/types.d.ts.map +1 -1
- package/dist/service/types.js +3 -1
- package/dist/service/types.js.map +1 -1
- package/dist/storage/eventStore.d.ts.map +1 -1
- package/dist/storage/eventStore.js +2 -0
- package/dist/storage/eventStore.js.map +1 -1
- package/dist/terminal/terminalManager.d.ts.map +1 -1
- package/dist/terminal/terminalManager.js +2 -0
- package/dist/terminal/terminalManager.js.map +1 -1
- package/dist/tombstoneCheck.d.ts.map +1 -1
- package/dist/tombstoneCheck.js +2 -0
- package/dist/tombstoneCheck.js.map +1 -1
- package/package.json +3 -3
package/LICENSE
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2026
|
|
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
|
@@ -47,6 +47,6 @@ just attach to it to add repos or re-read pairing info.
|
|
|
47
47
|
- **CLI subcommands, debug commands, data layout** —
|
|
48
48
|
[`docs/references/agent-cli.md`](../../docs/references/agent-cli.md)
|
|
49
49
|
- **Architecture (sessions, MessageBus, permissions, IPC)** —
|
|
50
|
-
[`docs/references/quicksave-architecture.md`](../../docs/references/quicksave-architecture.md)
|
|
50
|
+
[`docs/references/quicksave-architecture.en.md`](../../docs/references/quicksave-architecture.en.md)
|
|
51
51
|
- **Local development** —
|
|
52
52
|
[`docs/development.md`](../../docs/development.md)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"asyncQueue.d.ts","sourceRoot":"","sources":["../../src/ai/asyncQueue.ts"],"names":[],"mappings":"
|
|
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"}
|
package/dist/ai/asyncQueue.js
CHANGED
|
@@ -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":"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"}
|
|
@@ -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"}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 King Young Technology
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// Persistent attachment store on the agent.
|
|
5
|
+
//
|
|
6
|
+
// Once `attachmentStaging.consume(...)` materializes an `Attachment[]` for a
|
|
7
|
+
// `claude:start` / `claude:resume`, the messageHandler also writes each
|
|
8
|
+
// attachment to disk under
|
|
9
|
+
//
|
|
10
|
+
// <state>/attachments/<sessionId>/<attachmentId>.bin (base64-decoded bytes)
|
|
11
|
+
// <state>/attachments/<sessionId>/<attachmentId>.meta.json
|
|
12
|
+
//
|
|
13
|
+
// The PWA fetches bytes on demand via `attachment:fetch { sessionId, id }`,
|
|
14
|
+
// caches them in `attachmentCache` (L1/L2), and renders chip thumbnails
|
|
15
|
+
// without dragging the data through every card-history snapshot.
|
|
16
|
+
//
|
|
17
|
+
// Cleanup happens on session end-task: `removeSessionAttachments(sessionId)`
|
|
18
|
+
// nukes the directory.
|
|
19
|
+
// ============================================================================
|
|
20
|
+
import { mkdir, readFile, writeFile, appendFile, rm, readdir, stat } from 'fs/promises';
|
|
21
|
+
import { join } from 'path';
|
|
22
|
+
import { getAttachmentsDir } from '../service/singleton.js';
|
|
23
|
+
function dirFor(sessionId) {
|
|
24
|
+
return join(getAttachmentsDir(), sessionId);
|
|
25
|
+
}
|
|
26
|
+
function binPathFor(sessionId, attachmentId) {
|
|
27
|
+
return join(dirFor(sessionId), `${attachmentId}.bin`);
|
|
28
|
+
}
|
|
29
|
+
function metaPathFor(sessionId, attachmentId) {
|
|
30
|
+
return join(dirFor(sessionId), `${attachmentId}.meta.json`);
|
|
31
|
+
}
|
|
32
|
+
function turnManifestPathFor(sessionId) {
|
|
33
|
+
return join(dirFor(sessionId), 'turn-manifest.jsonl');
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Persist a batch of resolved attachments under one session id. Idempotent
|
|
37
|
+
* per id — re-writing the same id overwrites both files.
|
|
38
|
+
*/
|
|
39
|
+
export async function persistAttachments(sessionId, attachments) {
|
|
40
|
+
if (attachments.length === 0)
|
|
41
|
+
return;
|
|
42
|
+
await mkdir(dirFor(sessionId), { recursive: true });
|
|
43
|
+
await Promise.all(attachments.map(async (a) => {
|
|
44
|
+
const bytes = Buffer.from(a.data, 'base64');
|
|
45
|
+
await writeFile(binPathFor(sessionId, a.id), bytes);
|
|
46
|
+
const meta = {
|
|
47
|
+
id: a.id,
|
|
48
|
+
kind: a.kind,
|
|
49
|
+
mimeType: a.mimeType,
|
|
50
|
+
name: a.name,
|
|
51
|
+
size: a.size,
|
|
52
|
+
storedAt: Date.now(),
|
|
53
|
+
};
|
|
54
|
+
await writeFile(metaPathFor(sessionId, a.id), JSON.stringify(meta));
|
|
55
|
+
}));
|
|
56
|
+
// Append a turn-manifest line so the cardBuilder can rebind on JSONL replay
|
|
57
|
+
// by exact UUID instead of falling back to fuzzy (kind+size+name) match.
|
|
58
|
+
// One line per attachment-bearing user turn, in send order.
|
|
59
|
+
const line = JSON.stringify({ ids: attachments.map((a) => a.id) }) + '\n';
|
|
60
|
+
await appendFile(turnManifestPathFor(sessionId), line);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Read the per-session attachment turn manifest in send order. Returns an
|
|
64
|
+
* empty array if no manifest exists yet (e.g. a session whose attachments
|
|
65
|
+
* predate this code), letting callers fall back to fuzzy rebind.
|
|
66
|
+
*/
|
|
67
|
+
export async function readTurnManifest(sessionId) {
|
|
68
|
+
let raw;
|
|
69
|
+
try {
|
|
70
|
+
raw = await readFile(turnManifestPathFor(sessionId), 'utf8');
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
const out = [];
|
|
76
|
+
for (const line of raw.split('\n')) {
|
|
77
|
+
if (!line.trim())
|
|
78
|
+
continue;
|
|
79
|
+
try {
|
|
80
|
+
const parsed = JSON.parse(line);
|
|
81
|
+
if (parsed && Array.isArray(parsed.ids) && parsed.ids.every((x) => typeof x === 'string')) {
|
|
82
|
+
out.push({ ids: parsed.ids });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// skip malformed line — don't break the whole replay
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Read one attachment's bytes back, returning the wire `Attachment` shape
|
|
93
|
+
* (metadata + base64 data). Returns `null` if the attachment isn't on disk
|
|
94
|
+
* — e.g. session was end-tasked, or the JSONL had an attachment we didn't
|
|
95
|
+
* persist (cold-start session before this code shipped).
|
|
96
|
+
*/
|
|
97
|
+
export async function loadAttachment(sessionId, attachmentId) {
|
|
98
|
+
let metaRaw;
|
|
99
|
+
let bytes;
|
|
100
|
+
try {
|
|
101
|
+
[metaRaw, bytes] = await Promise.all([
|
|
102
|
+
readFile(metaPathFor(sessionId, attachmentId), 'utf8'),
|
|
103
|
+
readFile(binPathFor(sessionId, attachmentId)),
|
|
104
|
+
]);
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
let meta;
|
|
110
|
+
try {
|
|
111
|
+
meta = JSON.parse(metaRaw);
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
id: meta.id,
|
|
118
|
+
kind: meta.kind,
|
|
119
|
+
mimeType: meta.mimeType,
|
|
120
|
+
name: meta.name,
|
|
121
|
+
size: meta.size,
|
|
122
|
+
data: bytes.toString('base64'),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/** Drop every persisted attachment for one session (called on end-task). */
|
|
126
|
+
export async function removeSessionAttachments(sessionId) {
|
|
127
|
+
try {
|
|
128
|
+
await rm(dirFor(sessionId), { recursive: true, force: true });
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
// best effort — orphaned files are tolerable, they're local-only
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* List metadata for every attachment persisted under a session id. Mainly
|
|
136
|
+
* for diagnostics / tests.
|
|
137
|
+
*/
|
|
138
|
+
export async function listSessionAttachments(sessionId) {
|
|
139
|
+
let entries;
|
|
140
|
+
try {
|
|
141
|
+
entries = await readdir(dirFor(sessionId));
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
return [];
|
|
145
|
+
}
|
|
146
|
+
const metas = [];
|
|
147
|
+
for (const name of entries) {
|
|
148
|
+
if (!name.endsWith('.meta.json'))
|
|
149
|
+
continue;
|
|
150
|
+
try {
|
|
151
|
+
const raw = await readFile(join(dirFor(sessionId), name), 'utf8');
|
|
152
|
+
metas.push(JSON.parse(raw));
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
// skip unreadable metas
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return metas;
|
|
159
|
+
}
|
|
160
|
+
/** True if the meta+bin pair exist on disk — without reading the bytes. */
|
|
161
|
+
export async function hasPersistedAttachment(sessionId, attachmentId) {
|
|
162
|
+
try {
|
|
163
|
+
await Promise.all([
|
|
164
|
+
stat(metaPathFor(sessionId, attachmentId)),
|
|
165
|
+
stat(binPathFor(sessionId, attachmentId)),
|
|
166
|
+
]);
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=attachmentStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attachmentStore.js","sourceRoot":"","sources":["../../src/ai/attachmentStore.ts"],"names":[],"mappings":"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"}
|
package/dist/ai/cardBuilder.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Card, CardId, CardEvent, CardHistoryResponse, PendingInputAttachment } from '@sumicom/quicksave-shared';
|
|
1
|
+
import type { Attachment, Card, CardId, CardEvent, CardHistoryResponse, PendingInputAttachment } from '@sumicom/quicksave-shared';
|
|
2
2
|
/**
|
|
3
3
|
* Load persisted card history for a memory-mode session.
|
|
4
4
|
* Returns cards in insertion order, or empty array if none exist.
|
|
@@ -67,7 +67,7 @@ export declare class StreamCardBuilder {
|
|
|
67
67
|
private updateEvent;
|
|
68
68
|
private appendTextEvent;
|
|
69
69
|
private removeEvent;
|
|
70
|
-
userMessage(text: string): CardEvent;
|
|
70
|
+
userMessage(text: string, attachments?: readonly Attachment[]): CardEvent;
|
|
71
71
|
thinkingBlock(text: string): CardEvent;
|
|
72
72
|
/**
|
|
73
73
|
* Append text to current assistant_text card, or create a new one.
|
|
@@ -106,6 +106,7 @@ export declare class StreamCardBuilder {
|
|
|
106
106
|
subagentProgress(agentId: string, toolUseId: string | undefined, toolUseCount?: number, lastToolName?: string): CardEvent | null;
|
|
107
107
|
subagentEnd(agentId: string, toolUseId: string | undefined, status: 'completed' | 'failed' | 'stopped', summary?: string): CardEvent | null;
|
|
108
108
|
systemMessage(text: string, subtype?: 'compacted' | 'cost' | 'error' | 'info' | 'warning'): CardEvent;
|
|
109
|
+
recoverySuggested(reason: string, action: 'compact', label: string): CardEvent;
|
|
109
110
|
errorMessage(text: string): CardEvent;
|
|
110
111
|
}
|
|
111
112
|
export declare function buildCardsFromHistory(sessionId: string, cwd: string, offset?: number, limit?: number, cutoffBytes?: number): Promise<CardHistoryResponse>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cardBuilder.d.ts","sourceRoot":"","sources":["../../src/ai/cardBuilder.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cardBuilder.d.ts","sourceRoot":"","sources":["../../src/ai/cardBuilder.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,UAAU,EAEV,IAAI,EACJ,MAAM,EACN,SAAS,EAKT,mBAAmB,EAGnB,sBAAsB,EACvB,MAAM,2BAA2B,CAAC;AAmDnC;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAU3E;AAuKD,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,GAAG,CAAK;IAChB,OAAO,CAAC,KAAK,CAA2B;IACxC,oEAAoE;IACpE,OAAO,CAAC,iBAAiB,CAA6B;IACtD,gEAAgE;IAChE,OAAO,CAAC,eAAe,CAA6B;IACpD,yEAAyE;IACzE,OAAO,CAAC,cAAc,CAAqB;IAC3C,qEAAqE;IACrE,OAAO,CAAC,iBAAiB,CAAuB;IAChD,yFAAyF;IACzF,OAAO,CAAC,YAAY,CAAuB;IAC3C;4EACwE;IACxE,OAAO,CAAC,kBAAkB,CAAuB;gBAErC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM;IAK1C,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAIxC,sEAAsE;IACtE,YAAY,IAAI,IAAI;IAIpB,qFAAqF;IACrF,UAAU,IAAI,IAAI;IAQlB;;;;OAIG;IACG,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IA+BnC,qFAAqF;IAC/E,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAUrC;;;;;;;;;;;;;;;OAeG;IACG,qBAAqB,CAAC,IAAI,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAwC7G;+DAC2D;IAC3D,mBAAmB,IAAI,IAAI;IAI3B,IAAI,WAAW,IAAI,MAAM,GAAG,IAAI,CAE/B;IAED,IAAI,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,EAEnC;IAED,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,QAAQ;IAKhB,OAAO,CAAC,WAAW;IAgBnB,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,WAAW;IAOnB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,SAAS,UAAU,EAAE,GAAG,SAAS;IAuBzE,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS;IAMtC;;;OAGG;IACH,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS;IAUtC,8DAA8D;IAC9D,qBAAqB,IAAI,SAAS,GAAG,IAAI;IAOzC;;;;OAIG;IACH,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS;IAmB3F;;;OAGG;IACH,sBAAsB,CACpB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClC,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,sBAAsB;IACpC,uFAAuF;IACvF,SAAS,UAAQ,GAChB,SAAS;IAqBZ,uEAAuE;IACvE,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,sBAAsB,GAAG,SAAS,GAAG,IAAI;IAMhG,6EAA6E;IAC7E,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAetD,8CAA8C;IAC9C,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAO1D,qEAAqE;IACrE,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAIvC,gFAAgF;IAChF,QAAQ,IAAI,IAAI,EAAE;IAIlB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,SAAS,GAAG,IAAI;IAYlF,mEAAmE;IACnE,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,GAAG,IAAI;IAMpF,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS;IAiBlF,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,YAAY,CAAC,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAUhI,WAAW,CACT,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,MAAM,EAAE,WAAW,GAAG,QAAQ,GAAG,SAAS,EAC1C,OAAO,CAAC,EAAE,MAAM,GACf,SAAS,GAAG,IAAI;IAOnB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS;IAKrG,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS;IAY9E,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS;CAGtC;AAMD,wBAAsB,qBAAqB,CACzC,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,MAAM,SAAI,EACV,KAAK,SAAK,EACV,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,mBAAmB,CAAC,CA6a9B"}
|