@krischoichoi/channel-files 0.6.0

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 (122) hide show
  1. package/README.md +79 -0
  2. package/lib/attachment-resolver.d.ts +24 -0
  3. package/lib/attachment-resolver.d.ts.map +1 -0
  4. package/lib/attachment-resolver.js +38 -0
  5. package/lib/attachment-resolver.js.map +1 -0
  6. package/lib/attachments/extractors/docx.d.ts +6 -0
  7. package/lib/attachments/extractors/docx.d.ts.map +1 -0
  8. package/lib/attachments/extractors/docx.js +43 -0
  9. package/lib/attachments/extractors/docx.js.map +1 -0
  10. package/lib/attachments/extractors/index.d.ts +10 -0
  11. package/lib/attachments/extractors/index.d.ts.map +1 -0
  12. package/lib/attachments/extractors/index.js +10 -0
  13. package/lib/attachments/extractors/index.js.map +1 -0
  14. package/lib/attachments/extractors/pdf.d.ts +4 -0
  15. package/lib/attachments/extractors/pdf.d.ts.map +1 -0
  16. package/lib/attachments/extractors/pdf.js +44 -0
  17. package/lib/attachments/extractors/pdf.js.map +1 -0
  18. package/lib/attachments/extractors/registry.d.ts +43 -0
  19. package/lib/attachments/extractors/registry.d.ts.map +1 -0
  20. package/lib/attachments/extractors/registry.js +82 -0
  21. package/lib/attachments/extractors/registry.js.map +1 -0
  22. package/lib/attachments/extractors/text.d.ts +17 -0
  23. package/lib/attachments/extractors/text.d.ts.map +1 -0
  24. package/lib/attachments/extractors/text.js +0 -0
  25. package/lib/attachments/extractors/text.js.map +1 -0
  26. package/lib/attachments/extractors/types.d.ts +62 -0
  27. package/lib/attachments/extractors/types.d.ts.map +1 -0
  28. package/lib/attachments/extractors/types.js +13 -0
  29. package/lib/attachments/extractors/types.js.map +1 -0
  30. package/lib/attachments/extractors/xlsx.d.ts +6 -0
  31. package/lib/attachments/extractors/xlsx.d.ts.map +1 -0
  32. package/lib/attachments/extractors/xlsx.js +83 -0
  33. package/lib/attachments/extractors/xlsx.js.map +1 -0
  34. package/lib/attachments/filename.d.ts +23 -0
  35. package/lib/attachments/filename.d.ts.map +1 -0
  36. package/lib/attachments/filename.js +62 -0
  37. package/lib/attachments/filename.js.map +1 -0
  38. package/lib/attachments/hash.d.ts +5 -0
  39. package/lib/attachments/hash.d.ts.map +1 -0
  40. package/lib/attachments/hash.js +13 -0
  41. package/lib/attachments/hash.js.map +1 -0
  42. package/lib/attachments/index.d.ts +18 -0
  43. package/lib/attachments/index.d.ts.map +1 -0
  44. package/lib/attachments/index.js +18 -0
  45. package/lib/attachments/index.js.map +1 -0
  46. package/lib/attachments/mime.d.ts +27 -0
  47. package/lib/attachments/mime.d.ts.map +1 -0
  48. package/lib/attachments/mime.js +162 -0
  49. package/lib/attachments/mime.js.map +1 -0
  50. package/lib/attachments/pipeline-extractor.d.ts +8 -0
  51. package/lib/attachments/pipeline-extractor.d.ts.map +1 -0
  52. package/lib/attachments/pipeline-extractor.js +62 -0
  53. package/lib/attachments/pipeline-extractor.js.map +1 -0
  54. package/lib/attachments/pipeline.d.ts +29 -0
  55. package/lib/attachments/pipeline.d.ts.map +1 -0
  56. package/lib/attachments/pipeline.js +59 -0
  57. package/lib/attachments/pipeline.js.map +1 -0
  58. package/lib/attachments/policy.d.ts +25 -0
  59. package/lib/attachments/policy.d.ts.map +1 -0
  60. package/lib/attachments/policy.js +21 -0
  61. package/lib/attachments/policy.js.map +1 -0
  62. package/lib/attachments/render.d.ts +16 -0
  63. package/lib/attachments/render.d.ts.map +1 -0
  64. package/lib/attachments/render.js +28 -0
  65. package/lib/attachments/render.js.map +1 -0
  66. package/lib/attachments/schema.d.ts +58 -0
  67. package/lib/attachments/schema.d.ts.map +1 -0
  68. package/lib/attachments/schema.js +50 -0
  69. package/lib/attachments/schema.js.map +1 -0
  70. package/lib/attachments/store.d.ts +69 -0
  71. package/lib/attachments/store.d.ts.map +1 -0
  72. package/lib/attachments/store.js +254 -0
  73. package/lib/attachments/store.js.map +1 -0
  74. package/lib/attachments/tool-read.d.ts +34 -0
  75. package/lib/attachments/tool-read.d.ts.map +1 -0
  76. package/lib/attachments/tool-read.js +182 -0
  77. package/lib/attachments/tool-read.js.map +1 -0
  78. package/lib/attachments/types.d.ts +96 -0
  79. package/lib/attachments/types.d.ts.map +1 -0
  80. package/lib/attachments/types.js +18 -0
  81. package/lib/attachments/types.js.map +1 -0
  82. package/lib/backends/harness-native.d.ts +45 -0
  83. package/lib/backends/harness-native.d.ts.map +1 -0
  84. package/lib/backends/harness-native.js +56 -0
  85. package/lib/backends/harness-native.js.map +1 -0
  86. package/lib/catalog/legacy-backfill.d.ts +34 -0
  87. package/lib/catalog/legacy-backfill.d.ts.map +1 -0
  88. package/lib/catalog/legacy-backfill.js +49 -0
  89. package/lib/catalog/legacy-backfill.js.map +1 -0
  90. package/lib/catalog/store.d.ts +121 -0
  91. package/lib/catalog/store.d.ts.map +1 -0
  92. package/lib/catalog/store.js +227 -0
  93. package/lib/catalog/store.js.map +1 -0
  94. package/lib/catalog/types.d.ts +83 -0
  95. package/lib/catalog/types.d.ts.map +1 -0
  96. package/lib/catalog/types.js +29 -0
  97. package/lib/catalog/types.js.map +1 -0
  98. package/lib/index.d.ts +12 -0
  99. package/lib/index.d.ts.map +1 -0
  100. package/lib/index.js +12 -0
  101. package/lib/index.js.map +1 -0
  102. package/lib/migration/migrate-on-read.d.ts +61 -0
  103. package/lib/migration/migrate-on-read.d.ts.map +1 -0
  104. package/lib/migration/migrate-on-read.js +61 -0
  105. package/lib/migration/migrate-on-read.js.map +1 -0
  106. package/lib/migration/verify.d.ts +20 -0
  107. package/lib/migration/verify.d.ts.map +1 -0
  108. package/lib/migration/verify.js +35 -0
  109. package/lib/migration/verify.js.map +1 -0
  110. package/lib/paths.d.ts +13 -0
  111. package/lib/paths.d.ts.map +1 -0
  112. package/lib/paths.js +23 -0
  113. package/lib/paths.js.map +1 -0
  114. package/lib/plugin.d.ts +4 -0
  115. package/lib/plugin.d.ts.map +1 -0
  116. package/lib/plugin.js +6 -0
  117. package/lib/plugin.js.map +1 -0
  118. package/lib/service.d.ts +27 -0
  119. package/lib/service.d.ts.map +1 -0
  120. package/lib/service.js +67 -0
  121. package/lib/service.js.map +1 -0
  122. package/package.json +59 -0
@@ -0,0 +1,34 @@
1
+ /**
2
+ * read_channel_attachment — the M4 Harness tool.
3
+ *
4
+ * Lets the model read a stored channel asset's extracted text, paginated by
5
+ * optional offset/limit. It accepts ONLY { attachment_id, offset, limit } —
6
+ * never session_id / path / channel_id / conversation_id, and its
7
+ * ACL is bound to the SESSION that owns the asset, never the workspace cwd.
8
+ *
9
+ * Agent-scoped registration: installReadChannelAttachmentTool
10
+ * registers through the official @deepseek-ai/dsh-tools ctx.tools.register on
11
+ * the agent's own scope, so it shadows nothing globally and is disposed with
12
+ * the agent.
13
+ */
14
+ import { HarnessError } from '@deepseek-ai/dsh-llm';
15
+ import { type AttachmentPolicy } from './policy.js';
16
+ import type { ChannelInboundAssetStore } from './store.js';
17
+ import type { Context } from '@deepseek-ai/cordis';
18
+ /** Stable, machine-routable tool failure code. */
19
+ export type AttachmentReadErrorCode = 'ATTACHMENT_NOT_FOUND' | 'ATTACHMENT_ACCESS_DENIED';
20
+ /** Typed ACL error carried as a HarnessError so the tool pipeline records a stable code. */
21
+ export declare class AttachmentReadError extends HarnessError {
22
+ constructor(code: AttachmentReadErrorCode, message: string);
23
+ }
24
+ export interface RegisterReadChannelAttachmentToolOptions {
25
+ /** The private channel asset store backing the tool. */
26
+ store: ChannelInboundAssetStore;
27
+ /** Byte caps; defaults to the attachment policy defaults. */
28
+ policy?: AttachmentPolicy;
29
+ }
30
+ /** The tool definition, ready to register on an Agent scope. */
31
+ export declare function registerReadChannelAttachmentTool(options: RegisterReadChannelAttachmentToolOptions): import("@deepseek-ai/dsh-tools").ToolDefinition;
32
+ /** Install the tool on an Agent scoped context. */
33
+ export declare function installReadChannelAttachmentTool(agentCtx: Context, options: RegisterReadChannelAttachmentToolOptions): Promise<void>;
34
+ //# sourceMappingURL=tool-read.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-read.d.ts","sourceRoot":"","sources":["../../src/attachments/tool-read.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEpD,OAAO,EAA6B,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAG/E,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAEnD,kDAAkD;AAClD,MAAM,MAAM,uBAAuB,GAAG,sBAAsB,GAAG,0BAA0B,CAAC;AAE1F,4FAA4F;AAC5F,qBAAa,mBAAoB,SAAQ,YAAY;gBACvC,IAAI,EAAE,uBAAuB,EAAE,OAAO,EAAE,MAAM;CAI3D;AAOD,MAAM,WAAW,wCAAwC;IACvD,wDAAwD;IACxD,KAAK,EAAE,wBAAwB,CAAC;IAChC,6DAA6D;IAC7D,MAAM,CAAC,EAAE,gBAAgB,CAAC;CAC3B;AAED,gEAAgE;AAChE,wBAAgB,iCAAiC,CAC/C,OAAO,EAAE,wCAAwC,mDAmJlD;AAED,mDAAmD;AACnD,wBAAsB,gCAAgC,CACpD,QAAQ,EAAE,OAAO,EACjB,OAAO,EAAE,wCAAwC,GAChD,OAAO,CAAC,IAAI,CAAC,CAKf"}
@@ -0,0 +1,182 @@
1
+ /**
2
+ * read_channel_attachment — the M4 Harness tool.
3
+ *
4
+ * Lets the model read a stored channel asset's extracted text, paginated by
5
+ * optional offset/limit. It accepts ONLY { attachment_id, offset, limit } —
6
+ * never session_id / path / channel_id / conversation_id, and its
7
+ * ACL is bound to the SESSION that owns the asset, never the workspace cwd.
8
+ *
9
+ * Agent-scoped registration: installReadChannelAttachmentTool
10
+ * registers through the official @deepseek-ai/dsh-tools ctx.tools.register on
11
+ * the agent's own scope, so it shadows nothing globally and is disposed with
12
+ * the agent.
13
+ */
14
+ import { HarnessError } from '@deepseek-ai/dsh-llm';
15
+ import { defineTool } from '@deepseek-ai/dsh-tools';
16
+ import { DEFAULT_ATTACHMENT_POLICY } from './policy.js';
17
+ import { renderDescriptor } from './render.js';
18
+ /** Typed ACL error carried as a HarnessError so the tool pipeline records a stable code. */
19
+ export class AttachmentReadError extends HarnessError {
20
+ constructor(code, message) {
21
+ super(message, code);
22
+ this.name = 'AttachmentReadError';
23
+ }
24
+ }
25
+ /** Default number of extracted lines returned when limit is omitted. */
26
+ const DEFAULT_LINE_LIMIT = 200;
27
+ /** Hard bound on the paginated window so a model can never request an unbounded dump. */
28
+ const MAX_LINE_LIMIT = 1000;
29
+ /** The tool definition, ready to register on an Agent scope. */
30
+ export function registerReadChannelAttachmentTool(options) {
31
+ const store = options.store;
32
+ const policy = options.policy ?? DEFAULT_ATTACHMENT_POLICY;
33
+ return defineTool({
34
+ name: 'read_channel_attachment',
35
+ description: 'Read the extracted text of a channel-file attachment. ' +
36
+ 'Returns a descriptor plus a bounded, paginated text window (offset/limit, 1-based lines).',
37
+ parameters: {
38
+ attachment_id: { type: 'string', required: true, description: 'The stored attachment id to read.' },
39
+ offset: { type: 'integer', description: '1-based line number to start from (default 1).' },
40
+ limit: { type: 'integer', description: 'Maximum number of lines to return (default 200, max 1000).' },
41
+ },
42
+ output: {
43
+ schema: {
44
+ type: 'object',
45
+ additionalProperties: false,
46
+ properties: {
47
+ attachmentId: { type: 'string' },
48
+ name: { type: 'string' },
49
+ mimeType: { type: 'string' },
50
+ bytes: { type: 'integer' },
51
+ readable: { type: 'boolean' },
52
+ offset: { type: 'integer' },
53
+ limit: { type: 'integer' },
54
+ totalLines: { type: 'integer' },
55
+ returned: { type: 'integer' },
56
+ truncated: { type: 'boolean' },
57
+ text: { type: 'string' },
58
+ },
59
+ },
60
+ // The rendered surface leads with the compact descriptor (id, name,
61
+ // mime, size, readable) and only then the bounded window the model
62
+ // asked for — never an unbounded raw dump.
63
+ render: (args, value) => {
64
+ const descriptor = {
65
+ attachmentId: String(value.attachmentId ?? ''),
66
+ name: String(value.name ?? ''),
67
+ mimeType: value.mimeType !== undefined ? String(value.mimeType) : undefined,
68
+ bytes: Number(value.bytes ?? 0),
69
+ durable: true,
70
+ readable: Boolean(value.readable),
71
+ };
72
+ const text = typeof value.text === 'string' ? value.text : '';
73
+ const window = text ? '\n' + text : '';
74
+ return [{ type: 'text', text: renderDescriptor(descriptor) + window }];
75
+ },
76
+ },
77
+ async execute(args, exec) {
78
+ // --- Session ACL: the key is sessionId, never cwd. ---
79
+ const currentSessionId = exec.agent ? String(exec.agent.id) : undefined;
80
+ const asset = await store.get(args.attachment_id);
81
+ if (!asset) {
82
+ throw new AttachmentReadError('ATTACHMENT_NOT_FOUND', 'attachment not found: ' + args.attachment_id);
83
+ }
84
+ if (currentSessionId === undefined || asset.sessionId !== currentSessionId) {
85
+ throw new AttachmentReadError('ATTACHMENT_ACCESS_DENIED', 'attachment belongs to a different session');
86
+ }
87
+ const descriptor = {
88
+ attachmentId: asset.attachmentId,
89
+ name: asset.name,
90
+ mimeType: asset.mimeType,
91
+ bytes: asset.bytes,
92
+ durable: true,
93
+ readable: asset.extraction.status === 'ready',
94
+ };
95
+ if (asset.extraction.status !== 'ready') {
96
+ return {
97
+ attachmentId: descriptor.attachmentId,
98
+ name: descriptor.name,
99
+ mimeType: descriptor.mimeType,
100
+ bytes: descriptor.bytes,
101
+ readable: false,
102
+ offset: 0,
103
+ limit: 0,
104
+ totalLines: 0,
105
+ returned: 0,
106
+ truncated: false,
107
+ text: '',
108
+ };
109
+ }
110
+ let extracted;
111
+ try {
112
+ extracted = await store.readExtracted(asset.attachmentId, {
113
+ maxBytes: policy.extract.maxOutputBytes,
114
+ signal: exec.signal,
115
+ });
116
+ }
117
+ catch (error) {
118
+ // Cancellation must propagate; a store over-cap (output too large) is
119
+ // degraded into a bounded window rather than a hard failure.
120
+ if (exec.signal.aborted)
121
+ throw error;
122
+ return {
123
+ attachmentId: descriptor.attachmentId,
124
+ name: descriptor.name,
125
+ mimeType: descriptor.mimeType,
126
+ bytes: descriptor.bytes,
127
+ readable: true,
128
+ offset: 1,
129
+ limit: 0,
130
+ totalLines: 0,
131
+ returned: 0,
132
+ truncated: true,
133
+ text: '',
134
+ };
135
+ }
136
+ const lines = (extracted ?? '').split(/\r\n|\r|\n/);
137
+ const totalLines = lines.length;
138
+ const rawOffset = args.offset;
139
+ const safeOffset = Number.isFinite(rawOffset) && (rawOffset ?? 0) > 0 ? Math.floor(rawOffset) : 1;
140
+ const rawLimit = args.limit;
141
+ const requested = Number.isFinite(rawLimit) && (rawLimit ?? 0) > 0 ? Math.floor(rawLimit) : DEFAULT_LINE_LIMIT;
142
+ const limit = Math.min(requested, MAX_LINE_LIMIT);
143
+ const start = Math.max(0, safeOffset - 1);
144
+ const window = lines.slice(start, start + limit);
145
+ const text = window.join('\n');
146
+ // Bound the returned text to the output cap (safety net over the store cap).
147
+ const capped = capByteLength(text, policy.extract.maxOutputBytes);
148
+ const returned = capped.length > 0 ? window.length : 0;
149
+ return {
150
+ attachmentId: descriptor.attachmentId,
151
+ name: descriptor.name,
152
+ mimeType: descriptor.mimeType,
153
+ bytes: descriptor.bytes,
154
+ readable: true,
155
+ offset: safeOffset,
156
+ limit,
157
+ totalLines,
158
+ returned,
159
+ truncated: start + returned < totalLines || capped !== text,
160
+ text: capped,
161
+ };
162
+ },
163
+ });
164
+ }
165
+ /** Install the tool on an Agent scoped context. */
166
+ export async function installReadChannelAttachmentTool(agentCtx, options) {
167
+ const fiber = agentCtx.inject(['tools'], function* readToolScope(ctx) {
168
+ yield ctx.tools.register(registerReadChannelAttachmentTool(options));
169
+ });
170
+ await fiber.await().then(() => undefined);
171
+ }
172
+ /** Trim a string to at most maxBytes UTF-8 bytes without splitting a char. */
173
+ function capByteLength(text, maxBytes) {
174
+ const encoded = new TextEncoder().encode(text);
175
+ if (encoded.byteLength <= maxBytes)
176
+ return text;
177
+ let end = maxBytes;
178
+ while (end > 0 && (encoded[end] & 0xc0) === 0x80)
179
+ end--;
180
+ return new TextDecoder('utf-8').decode(encoded.subarray(0, end));
181
+ }
182
+ //# sourceMappingURL=tool-read.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-read.js","sourceRoot":"","sources":["../../src/attachments/tool-read.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Stored channel asset metadata (plan \u00a745).
3
+ *
4
+ * A `StoredChannelAsset` is the durable, platform-agnostic record of a file /
5
+ * audio / video part that entered the private channel asset store. Images are
6
+ * NOT stored here — they use the Harness native image seam
7
+ * (`@deepseek-ai/dsh-attachment`, the `SaveImageHook` path), so `kind` is
8
+ * only `'file' | 'audio' | 'video'`.
9
+ *
10
+ * The metadata deliberately carries NO transient platform state (plan \u00a746):
11
+ * there is no `resourceRef`, no provider URL, no access token, no
12
+ * `contextToken`, no `sessionWebhook` and no encrypted AES key. Only what the
13
+ * harness needs to read the bytes back locally, bound to the session that owns
14
+ * them (the ACL is `sessionId`).
15
+ */
16
+ /** Schema version of `StoredChannelAsset` (mirrors the v1 directory). */
17
+ export declare const ASSET_SCHEMA_VERSION: 1;
18
+ export type ChannelAssetKind = 'file' | 'audio' | 'video' | 'image';
19
+ export type ExtractionStatus = 'not-needed' | 'ready' | 'unsupported' | 'failed' | 'too-large';
20
+ export type ExtractionFormat = 'text' | 'markdown';
21
+ export interface AssetExtraction {
22
+ status: ExtractionStatus;
23
+ /** Present when `status` is `ready`. */
24
+ format?: ExtractionFormat;
25
+ /** Byte length of the extracted text (when status is `ready`). */
26
+ bytes?: number;
27
+ /** Stable, de-identified failure reason (when status is `failed`). */
28
+ errorCode?: string;
29
+ }
30
+ export interface StoredChannelAsset {
31
+ schemaVersion: typeof ASSET_SCHEMA_VERSION;
32
+ attachmentId: string;
33
+ sessionId: string;
34
+ channelId: string;
35
+ accountId: string;
36
+ conversationId: string;
37
+ conversationType?: 'dm' | 'group';
38
+ threadId?: string;
39
+ messageId: string;
40
+ kind: ChannelAssetKind;
41
+ name: string;
42
+ mimeType?: string;
43
+ /** Final byte length (computed by the store from the written bytes). */
44
+ bytes: number;
45
+ sha256: string;
46
+ extraction: AssetExtraction;
47
+ createdAt: number;
48
+ }
49
+ /**
50
+ * Input to `ChannelInboundAssetStore.put`.
51
+ *
52
+ * `bytes` / `sha256` / `mimeType` are NOT caller-supplied truth: the store
53
+ * computes the final `sha256` and `bytes` from `data` and re-verifies
54
+ * `mimeType` via magic sniffing (plan \u00a747); an adapter `mimeType` is only
55
+ * a hint. `extraction` (when given) records the initial extraction state in
56
+ * `meta.json`; the extracted text itself arrives later via
57
+ * `putExtracted`.
58
+ */
59
+ export interface PutChannelAssetInput {
60
+ attachmentId: string;
61
+ sessionId: string;
62
+ channelId: string;
63
+ accountId: string;
64
+ conversationId: string;
65
+ conversationType?: 'dm' | 'group';
66
+ threadId?: string;
67
+ messageId: string;
68
+ kind: ChannelAssetKind;
69
+ name: string;
70
+ /** Adapter MIME hint — re-verified by the store, never trusted verbatim. */
71
+ mimeType?: string;
72
+ /** The raw bytes to persist (written verbatim as `raw.bin`). */
73
+ data: Uint8Array;
74
+ /** Initial extraction state; defaults to `{ status: 'not-needed' }`. */
75
+ extraction?: AssetExtraction;
76
+ createdAt?: number;
77
+ }
78
+ /** Input to `ChannelInboundAssetStore.putExtracted` (plan \u00a743). */
79
+ export interface StoredAssetExtractionInput {
80
+ /** The extracted readable text (written to `extracted.md`). */
81
+ text: string;
82
+ format: ExtractionFormat;
83
+ /** Byte length override; defaults to the UTF-8 byte length of `text`. */
84
+ bytes?: number;
85
+ }
86
+ /** User-visible descriptor for a stored asset (plan \u00a754). */
87
+ export interface StoredAssetDescriptor {
88
+ attachmentId: string;
89
+ name: string;
90
+ mimeType?: string;
91
+ bytes: number;
92
+ durable: boolean;
93
+ /** Whether exposed extracted text is ready (not the text itself). */
94
+ readable: boolean;
95
+ }
96
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/attachments/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,yEAAyE;AACzE,eAAO,MAAM,oBAAoB,EAAG,CAAU,CAAC;AAE/C,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;AAOpE,MAAM,MAAM,gBAAgB,GACxB,YAAY,GACZ,OAAO,GACP,aAAa,GACb,QAAQ,GACR,WAAW,CAAC;AAEhB,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,UAAU,CAAC;AAEnD,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,gBAAgB,CAAC;IACzB,wCAAwC;IACxC,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,kEAAkE;IAClE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,aAAa,EAAE,OAAO,oBAAoB,CAAC;IAC3C,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,IAAI,GAAG,OAAO,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,gBAAgB,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,eAAe,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,oBAAoB;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,IAAI,GAAG,OAAO,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,gBAAgB,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gEAAgE;IAChE,IAAI,EAAE,UAAU,CAAC;IACjB,wEAAwE;IACxE,UAAU,CAAC,EAAE,eAAe,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wEAAwE;AACxE,MAAM,WAAW,0BAA0B;IACzC,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,gBAAgB,CAAC;IACzB,yEAAyE;IACzE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,kEAAkE;AAClE,MAAM,WAAW,qBAAqB;IACpC,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,qEAAqE;IACrE,QAAQ,EAAE,OAAO,CAAC;CACnB"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Stored channel asset metadata (plan \u00a745).
3
+ *
4
+ * A `StoredChannelAsset` is the durable, platform-agnostic record of a file /
5
+ * audio / video part that entered the private channel asset store. Images are
6
+ * NOT stored here — they use the Harness native image seam
7
+ * (`@deepseek-ai/dsh-attachment`, the `SaveImageHook` path), so `kind` is
8
+ * only `'file' | 'audio' | 'video'`.
9
+ *
10
+ * The metadata deliberately carries NO transient platform state (plan \u00a746):
11
+ * there is no `resourceRef`, no provider URL, no access token, no
12
+ * `contextToken`, no `sessionWebhook` and no encrypted AES key. Only what the
13
+ * harness needs to read the bytes back locally, bound to the session that owns
14
+ * them (the ACL is `sessionId`).
15
+ */
16
+ /** Schema version of `StoredChannelAsset` (mirrors the v1 directory). */
17
+ export const ASSET_SCHEMA_VERSION = 1;
18
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/attachments/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,yEAAyE;AACzE,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAU,CAAC"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Native Harness Generic Attachment capability seam.
3
+ *
4
+ * This module defines the INTERFACE and the FAKE only. There is deliberately
5
+ * NO real Harness generic attachment implementation here, because the current
6
+ * Harness distribution only ships an IMAGE attachment seam
7
+ * (`@deepseek-ai/dsh-attachment` SaveImageHook / ImageAttachmentRef /
8
+ * ImageBlock) — an image-only service MUST NOT be mistaken for a generic
9
+ * attachment surface.
10
+ *
11
+ * No runtime detector is exported yet: guessing future method or capability
12
+ * names would create a false public contract. When Harness publishes a real
13
+ * generic attachment API, its public package types/schema will define the
14
+ * adapter and trust-boundary validation added here.
15
+ */
16
+ import { type BinaryKind } from '@krischoichoi/channel-core';
17
+ /** Backend-neutral capability consumed by the dormant migration seam. */
18
+ export interface NativeGenericAttachmentCapability {
19
+ /** Whether a generic attachment surface is available for use. */
20
+ available: boolean;
21
+ /** Whether the available backend accepts the given kind (and MIME hint). */
22
+ supports(kind: BinaryKind, mimeType?: string): boolean;
23
+ }
24
+ /**
25
+ * Constant production capability until Harness publishes a generic API.
26
+ */
27
+ export declare const NO_NATIVE_GENERIC_ATTACHMENT: NativeGenericAttachmentCapability;
28
+ export interface FakeNativeGenericAttachmentCapabilityOptions {
29
+ /** Defaults to `false` — a fake must opt in to availability. */
30
+ available?: boolean;
31
+ /** Kinds the fake accepts; defaults to the generic kinds when available. */
32
+ supportedKinds?: readonly BinaryKind[];
33
+ /**
34
+ * Optional MIME allow-list. When given, `supports` requires the `mimeType`
35
+ * argument to be present and listed (mirrors a future MIME-scoped API).
36
+ */
37
+ supportedMimeTypes?: readonly string[];
38
+ }
39
+ /**
40
+ * Configurable fake capability for tests. By default (no options) it behaves
41
+ * exactly like `NO_NATIVE_GENERIC_ATTACHMENT`; set `available: true` to
42
+ * simulate a future generic surface.
43
+ */
44
+ export declare function FakeNativeGenericAttachmentCapability(options?: FakeNativeGenericAttachmentCapabilityOptions): NativeGenericAttachmentCapability;
45
+ //# sourceMappingURL=harness-native.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harness-native.d.ts","sourceRoot":"","sources":["../../src/backends/harness-native.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAE3E,yEAAyE;AACzE,MAAM,WAAW,iCAAiC;IAChD,iEAAiE;IACjE,SAAS,EAAE,OAAO,CAAC;IACnB,4EAA4E;IAC5E,QAAQ,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACxD;AAED;;GAEG;AACH,eAAO,MAAM,4BAA4B,EAAE,iCAG1C,CAAC;AAUF,MAAM,WAAW,4CAA4C;IAC3D,gEAAgE;IAChE,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,4EAA4E;IAC5E,cAAc,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;IACvC;;;OAGG;IACH,kBAAkB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC;AAED;;;;GAIG;AACH,wBAAgB,qCAAqC,CACnD,OAAO,GAAE,4CAAiD,GACzD,iCAAiC,CAiBnC"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Native Harness Generic Attachment capability seam.
3
+ *
4
+ * This module defines the INTERFACE and the FAKE only. There is deliberately
5
+ * NO real Harness generic attachment implementation here, because the current
6
+ * Harness distribution only ships an IMAGE attachment seam
7
+ * (`@deepseek-ai/dsh-attachment` SaveImageHook / ImageAttachmentRef /
8
+ * ImageBlock) — an image-only service MUST NOT be mistaken for a generic
9
+ * attachment surface.
10
+ *
11
+ * No runtime detector is exported yet: guessing future method or capability
12
+ * names would create a false public contract. When Harness publishes a real
13
+ * generic attachment API, its public package types/schema will define the
14
+ * adapter and trust-boundary validation added here.
15
+ */
16
+ import { BINARY_KINDS } from '@krischoichoi/channel-core';
17
+ /**
18
+ * Constant production capability until Harness publishes a generic API.
19
+ */
20
+ export const NO_NATIVE_GENERIC_ATTACHMENT = {
21
+ available: false,
22
+ supports: () => false,
23
+ };
24
+ /**
25
+ * Kinds the generic backend covers. `image` is deliberately excluded: images
26
+ * belong to the Harness image seam, not to the generic attachment backend.
27
+ */
28
+ const GENERIC_KINDS = BINARY_KINDS.filter((kind) => kind !== 'image');
29
+ /**
30
+ * Configurable fake capability for tests. By default (no options) it behaves
31
+ * exactly like `NO_NATIVE_GENERIC_ATTACHMENT`; set `available: true` to
32
+ * simulate a future generic surface.
33
+ */
34
+ export function FakeNativeGenericAttachmentCapability(options = {}) {
35
+ const available = options.available ?? false;
36
+ const kinds = options.supportedKinds ?? (available ? GENERIC_KINDS : []);
37
+ const mimeTypes = options.supportedMimeTypes;
38
+ let supports;
39
+ if (mimeTypes === undefined) {
40
+ supports = (kind) => available && kinds.includes(kind);
41
+ }
42
+ else {
43
+ supports = (kind, mimeType) => {
44
+ if (!available)
45
+ return false;
46
+ if (!kinds.includes(kind))
47
+ return false;
48
+ // A MIME allow-list means an absent MIME hint cannot be supported.
49
+ if (mimeType === undefined)
50
+ return false;
51
+ return mimeTypes.includes(mimeType);
52
+ };
53
+ }
54
+ return { available, supports };
55
+ }
56
+ //# sourceMappingURL=harness-native.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harness-native.js","sourceRoot":"","sources":["../../src/backends/harness-native.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,YAAY,EAAmB,MAAM,4BAA4B,CAAC;AAU3E;;GAEG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAsC;IAC7E,SAAS,EAAE,KAAK;IAChB,QAAQ,EAAE,GAAG,EAAE,CAAC,KAAK;CACtB,CAAC;AAEF;;;GAGG;AACH,MAAM,aAAa,GAA0B,YAAY,CAAC,MAAM,CAC9D,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,OAAO,CAC3B,CAAC;AAcF;;;;GAIG;AACH,MAAM,UAAU,qCAAqC,CACnD,UAAwD,EAAE;IAE1D,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,KAAK,CAAC;IAC7C,MAAM,KAAK,GAAG,OAAO,CAAC,cAAc,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACzE,MAAM,SAAS,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAC7C,IAAI,QAAuD,CAAC;IAC5D,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5B,QAAQ,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACzD,CAAC;SAAM,CAAC;QACN,QAAQ,GAAG,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE;YAC5B,IAAI,CAAC,SAAS;gBAAE,OAAO,KAAK,CAAC;YAC7B,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC;gBAAE,OAAO,KAAK,CAAC;YACxC,mEAAmE;YACnE,IAAI,QAAQ,KAAK,SAAS;gBAAE,OAAO,KAAK,CAAC;YACzC,OAAO,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACtC,CAAC,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC;AACjC,CAAC"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Legacy -> Catalog v2 backfill builder.
3
+ *
4
+ * A pure function mapping a `StoredChannelAsset` (schemaVersion 1) to an
5
+ * `AttachmentCatalogRecordV2` that points `storage.backend` at `channel-v1`.
6
+ * It only RE-INDEXES already-stored v1 data — it never rewrites or moves the
7
+ * v1 tree (旧数据不动 / 新 writer 写最新格式 / 旧 reader 永远兼容).
8
+ *
9
+ * This module performs NO file I/O by design: constructing the in-memory
10
+ * record is pure; all store I/O lives in `catalog/store.ts`.
11
+ */
12
+ import type { StoredChannelAsset } from '../attachments/types.js';
13
+ import { type AttachmentCatalogRecordV2 } from './types.js';
14
+ /**
15
+ * Guard: only generic (non-image) kinds can be cataloged. Images never enter
16
+ * the v1 store (the image mirror writes live assets only, issue #7), so this
17
+ * stays a type-level contract: the catalog v2 `file.kind` vocabulary is
18
+ * file/audio/video.
19
+ */
20
+ export declare function isCatalogableLegacyKind(asset: StoredChannelAsset): asset is StoredChannelAsset & {
21
+ kind: 'file' | 'audio' | 'video';
22
+ };
23
+ /**
24
+ * Build the catalog v2 index record for an existing legacy v1 asset.
25
+ *
26
+ * - `storage.backend` -> `'channel-v1'` (bytes still live in `attachments/v1`).
27
+ * - `migration.sourceBackend` -> `'channel-v1'` records where this record
28
+ * originated; no `migratedAt` / `verifiedAt` (nothing migrated yet).
29
+ * - provenance / file fields map 1:1 from the legacy metadata.
30
+ * - Fails loud on non-catalogable kinds (e.g. the issue #7 image mirror) —
31
+ * v1 trees can never contain them.
32
+ */
33
+ export declare function buildCatalogRecordFromLegacy(asset: StoredChannelAsset): AttachmentCatalogRecordV2;
34
+ //# sourceMappingURL=legacy-backfill.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"legacy-backfill.d.ts","sourceRoot":"","sources":["../../src/catalog/legacy-backfill.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAEL,KAAK,yBAAyB,EAC/B,MAAM,YAAY,CAAC;AAEpB;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,kBAAkB,GACxB,KAAK,IAAI,kBAAkB,GAAG;IAAE,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,CAAA;CAAE,CAEpE;AAED;;;;;;;;;GASG;AACH,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,kBAAkB,GACxB,yBAAyB,CA2B3B"}
@@ -0,0 +1,49 @@
1
+ import { CATALOG_SCHEMA_VERSION, } from './types.js';
2
+ /**
3
+ * Guard: only generic (non-image) kinds can be cataloged. Images never enter
4
+ * the v1 store (the image mirror writes live assets only, issue #7), so this
5
+ * stays a type-level contract: the catalog v2 `file.kind` vocabulary is
6
+ * file/audio/video.
7
+ */
8
+ export function isCatalogableLegacyKind(asset) {
9
+ return asset.kind === 'file' || asset.kind === 'audio' || asset.kind === 'video';
10
+ }
11
+ /**
12
+ * Build the catalog v2 index record for an existing legacy v1 asset.
13
+ *
14
+ * - `storage.backend` -> `'channel-v1'` (bytes still live in `attachments/v1`).
15
+ * - `migration.sourceBackend` -> `'channel-v1'` records where this record
16
+ * originated; no `migratedAt` / `verifiedAt` (nothing migrated yet).
17
+ * - provenance / file fields map 1:1 from the legacy metadata.
18
+ * - Fails loud on non-catalogable kinds (e.g. the issue #7 image mirror) —
19
+ * v1 trees can never contain them.
20
+ */
21
+ export function buildCatalogRecordFromLegacy(asset) {
22
+ if (!isCatalogableLegacyKind(asset)) {
23
+ throw new Error(`legacy catalog backfill received a non-catalogable kind '${asset.kind}'`);
24
+ }
25
+ return {
26
+ schemaVersion: CATALOG_SCHEMA_VERSION,
27
+ attachmentId: asset.attachmentId,
28
+ owner: { sessionId: asset.sessionId },
29
+ provenance: {
30
+ channelId: asset.channelId,
31
+ accountId: asset.accountId,
32
+ conversationId: asset.conversationId,
33
+ ...(asset.conversationType ? { conversationType: asset.conversationType } : {}),
34
+ ...(asset.threadId ? { threadId: asset.threadId } : {}),
35
+ messageId: asset.messageId,
36
+ },
37
+ file: {
38
+ kind: asset.kind,
39
+ name: asset.name,
40
+ ...(asset.mimeType ? { mimeType: asset.mimeType } : {}),
41
+ bytes: asset.bytes,
42
+ sha256: asset.sha256,
43
+ },
44
+ storage: { backend: 'channel-v1' },
45
+ migration: { sourceBackend: 'channel-v1' },
46
+ createdAt: asset.createdAt,
47
+ };
48
+ }
49
+ //# sourceMappingURL=legacy-backfill.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"legacy-backfill.js","sourceRoot":"","sources":["../../src/catalog/legacy-backfill.ts"],"names":[],"mappings":"AAYA,OAAO,EACL,sBAAsB,GAEvB,MAAM,YAAY,CAAC;AAEpB;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CACrC,KAAyB;IAEzB,OAAO,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC;AACnF,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,4BAA4B,CAC1C,KAAyB;IAEzB,IAAI,CAAC,uBAAuB,CAAC,KAAK,CAAC,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,4DAA4D,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC;IAC7F,CAAC;IACD,OAAO;QACL,aAAa,EAAE,sBAAsB;QACrC,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,KAAK,EAAE,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE;QACrC,UAAU,EAAE;YACV,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,GAAG,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,KAAK,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/E,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACvD,SAAS,EAAE,KAAK,CAAC,SAAS;SAC3B;QACD,IAAI,EAAE;YACJ,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACvD,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,MAAM,EAAE,KAAK,CAAC,MAAM;SACrB;QACD,OAAO,EAAE,EAAE,OAAO,EAAE,YAAY,EAAE;QAClC,SAAS,EAAE,EAAE,aAAa,EAAE,YAAY,EAAE;QAC1C,SAAS,EAAE,KAAK,CAAC,SAAS;KAC3B,CAAC;AACJ,CAAC"}