zigmini 1.0.0-beta.2 → 1.1.0-beta.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.
- package/README.ko.md +36 -3
- package/README.md +36 -3
- package/assets/agent/gateway-request-rules.md +1 -1
- package/assets/agent/gateway-rules.md +6 -1
- package/assets/android/zigmini.apk +0 -0
- package/assets/android/zigmini.apk.json +3 -3
- package/assets/build.json +1 -1
- package/assets/document-reader/document-reader +0 -0
- package/assets/document-reader/host-selector +0 -0
- package/assets/document-reader/manifest.json +27 -0
- package/docs/manual.ko.md +120 -11
- package/docs/manual.md +123 -12
- package/host/document-reader/DocumentRead.swift +370 -0
- package/host/document-reader/HostSelector.swift +192 -0
- package/host/document-reader/Protocol.swift +647 -0
- package/host/document-reader/main.swift +22 -0
- package/host/omo-bridge/agent-aside-tool.ts +119 -0
- package/host/omo-bridge/agent-aside.ts +522 -0
- package/host/omo-bridge/agent-collaboration-files.ts +282 -0
- package/host/omo-bridge/agent-collaboration.ts +385 -0
- package/host/omo-bridge/agent-control.ts +400 -28
- package/host/omo-bridge/agent-execution.sb +42 -0
- package/host/omo-bridge/agent-execution.ts +158 -0
- package/host/omo-bridge/agent-tool-adapters.ts +388 -0
- package/host/omo-bridge/agent-tool-permissions.ts +96 -0
- package/host/omo-bridge/aside-tabs.ts +41 -0
- package/host/omo-bridge/attachment-read.ts +100 -0
- package/host/omo-bridge/bridge.ts +10 -0
- package/host/omo-bridge/managed-call.ts +46 -0
- package/host/omo-bridge/task-control.ts +39 -5
- package/host/omo-check/patches/attachments-293/README.md +138 -0
- package/host/omo-check/patches/attachments-293/document-content.js +234 -0
- package/host/omo-check/patches/attachments-293/document-content.types.txt +91 -0
- package/host/omo-check/patches/attachments-293/omo-5.1.51-documents.patch +831 -0
- package/host/omo-check/patches/attachments-293/omo-5.1.51-gateway-port.manifest.json +471 -0
- package/host/omo-check/patches/omo-5.1.51-agent-tool-authority.patch +471 -0
- package/host/omo-check/patches/omo-5.1.51-chatgpt-terra.patch +21 -0
- package/host/omo-check/patches/omo-5.1.51-gateway-port.manifest.json +44 -6
- package/host/omo-check/patches/omo-5.1.51-memory-receipts.patch +5 -0
- package/package.json +5 -3
- package/src/agent/api.ts +345 -20
- package/src/agent/artifacts.ts +196 -0
- package/src/agent/aside-target-routes.ts +369 -0
- package/src/agent/aside-targets.ts +215 -0
- package/src/agent/collaboration-admission.ts +47 -0
- package/src/agent/collaboration-read.ts +117 -0
- package/src/agent/collaboration-session.ts +196 -0
- package/src/agent/collaboration-types.ts +258 -0
- package/src/agent/collaboration.ts +399 -0
- package/src/agent/definition.ts +65 -3
- package/src/agent/fields.ts +9 -3
- package/src/agent/file-core.ts +325 -0
- package/src/agent/files.ts +87 -8
- package/src/agent/list.ts +3 -3
- package/src/agent/managed-call.ts +144 -0
- package/src/agent/tool-catalog.ts +184 -0
- package/src/agent/tool-delivery.ts +359 -0
- package/src/agent/tool-permission-types.ts +81 -0
- package/src/agent/tool-permissions.ts +1366 -0
- package/src/attachments/api.ts +161 -0
- package/src/attachments/document-reader.ts +325 -0
- package/src/attachments/host-selector.ts +410 -0
- package/src/attachments/managed-binding.ts +83 -0
- package/src/attachments/prepare.ts +214 -0
- package/src/attachments/selector-port.ts +28 -0
- package/src/attachments/service.ts +729 -0
- package/src/attachments/store.ts +698 -0
- package/src/attachments/types.ts +123 -0
- package/src/cli/doctor.ts +30 -10
- package/src/decisions.ts +109 -10
- package/src/engine/apply-patch.ts +21 -0
- package/src/engine/compat.json +2 -1
- package/src/engine/install.ts +45 -4
- package/src/engine/sandbox-cell.ts +2 -0
- package/src/gc.ts +33 -225
- package/src/housekeeping/api.ts +163 -0
- package/src/housekeeping/audit.ts +61 -0
- package/src/housekeeping/collectors/agent-collaboration.ts +33 -0
- package/src/housekeeping/collectors/agent-drafts.ts +43 -0
- package/src/housekeeping/collectors/agent-file-trash.ts +36 -0
- package/src/housekeeping/collectors/agent-trash.ts +28 -0
- package/src/housekeeping/collectors/attachment-cleanup.ts +35 -0
- package/src/housekeeping/collectors/db-backup.ts +25 -0
- package/src/housekeeping/collectors/docker-build-cache.ts +29 -0
- package/src/housekeeping/collectors/docker-image.ts +135 -0
- package/src/housekeeping/collectors/omo-version.ts +32 -0
- package/src/housekeeping/collectors/orphan-test-process.ts +205 -0
- package/src/housekeeping/collectors/reconcile-work.ts +55 -0
- package/src/housekeeping/collectors/rpc-daemon.ts +39 -0
- package/src/housekeeping/collectors/shared.ts +149 -0
- package/src/housekeeping/collectors/temp-dir.ts +28 -0
- package/src/housekeeping/collectors/update-file.ts +25 -0
- package/src/housekeeping/collectors/worktree.ts +193 -0
- package/src/housekeeping/engine.ts +125 -0
- package/src/housekeeping/reconcile/work.ts +130 -0
- package/src/housekeeping/registry.ts +63 -0
- package/src/housekeeping/trash.ts +59 -0
- package/src/housekeeping/types.ts +31 -0
- package/src/hub.ts +1095 -41
- package/src/main.ts +230 -31
- package/src/message-references.ts +14 -5
- package/src/profile-prompt.ts +13 -1
- package/src/profile.ts +13 -5
- package/src/protocol.ts +41 -6
- package/src/security-allow.ts +79 -2
- package/src/security-check.ts +1 -1
- package/src/store.ts +1946 -23
- package/src/test-crash.ts +20 -0
- package/src/usage.ts +18 -7
- package/src/work-watch.ts +55 -7
- package/src/work.ts +192 -3
- package/web/DESIGN.md +35 -1
- package/web/agents.d.ts +189 -12
- package/web/agents.js +898 -39
- package/web/app.js +2634 -210
- package/web/attachments.d.ts +91 -0
- package/web/attachments.js +422 -0
- package/web/collaboration.d.ts +87 -0
- package/web/collaboration.js +290 -0
- package/web/decisions.d.ts +39 -2
- package/web/decisions.js +354 -2
- package/web/gc.d.ts +26 -5
- package/web/gc.js +113 -13
- package/web/i18n/agents.d.ts +4 -0
- package/web/i18n/agents.js +200 -8
- package/web/i18n/attachments.js +78 -0
- package/web/i18n/decisions.d.ts +4 -0
- package/web/i18n/decisions.js +68 -0
- package/web/i18n/gc.d.ts +4 -0
- package/web/i18n/gc.js +184 -2
- package/web/i18n/main.js +80 -8
- package/web/i18n/tour.d.ts +4 -0
- package/web/i18n/tour.js +37 -0
- package/web/index.html +20 -3
- package/web/profile.d.ts +2 -2
- package/web/profile.js +16 -10
- package/web/style.css +342 -21
- package/web/tooltip.d.ts +2 -0
- package/web/tooltip.js +12 -0
- package/web/tour.d.ts +57 -0
- package/web/tour.js +450 -0
|
@@ -0,0 +1,831 @@
|
|
|
1
|
+
--- a/@code-yeongyu/senpi/dist/core/agent-session.d.ts
|
|
2
|
+
+++ b/@code-yeongyu/senpi/dist/core/agent-session.d.ts
|
|
3
|
+
@@ -1,1 +1,2 @@
|
|
4
|
+
-/**
|
|
5
|
+
+import type { DocumentContent, DocumentReference, DocumentAttachment, DocumentResolver } from "@earendil-works/pi-ai";
|
|
6
|
+
+/**
|
|
7
|
+
@@ -556,1 +557,3 @@
|
|
8
|
+
-export interface QueuedInputOptions extends ClientMessageIdentity {
|
|
9
|
+
+export interface QueuedInputOptions extends ClientMessageIdentity {
|
|
10
|
+
+ documents?: (DocumentContent | DocumentReference)[];
|
|
11
|
+
+ attachments?: DocumentAttachment[];
|
|
12
|
+
@@ -569,1 +572,3 @@
|
|
13
|
+
-export interface PromptOptions extends ClientMessageIdentity {
|
|
14
|
+
+export interface PromptOptions extends ClientMessageIdentity {
|
|
15
|
+
+ documents?: (DocumentContent | DocumentReference)[];
|
|
16
|
+
+ attachments?: DocumentAttachment[];
|
|
17
|
+
@@ -1508,1 +1513,2 @@
|
|
18
|
+
- prompt(text: string, options?: PromptOptions): Promise<void>;
|
|
19
|
+
+ setDocumentResolver(resolve: DocumentResolver | undefined): void;
|
|
20
|
+
+ prompt(text: string, options?: PromptOptions): Promise<void>;
|
|
21
|
+
--- a/@code-yeongyu/senpi/dist/core/agent-session.js
|
|
22
|
+
+++ b/@code-yeongyu/senpi/dist/core/agent-session.js
|
|
23
|
+
@@ -1,1 +1,2 @@
|
|
24
|
+
-/**
|
|
25
|
+
+import { normalizeDocumentInput, documentInputMetadata, resolveDocumentMessages } from "@earendil-works/pi-ai";
|
|
26
|
+
+/**
|
|
27
|
+
@@ -1482,1 +1483,5 @@
|
|
28
|
+
- const transformed = this._requestShapeRecovery.apply(previousTransformContext ? await previousTransformContext(messages, signal) : messages);
|
|
29
|
+
+ const prepared = this._requestShapeRecovery.apply(previousTransformContext ? await previousTransformContext(messages, signal) : messages);
|
|
30
|
+
+ const transformed = await resolveDocumentMessages(prepared, this.model, {
|
|
31
|
+
+ resolve: this._documentResolver, sessionId: this.sessionManager.getSessionId(),
|
|
32
|
+
+ inputId: messages.findLast(message => message.role === "user")?.clientInputId, signal,
|
|
33
|
+
+ });
|
|
34
|
+
@@ -4154,1 +4159,13 @@
|
|
35
|
+
- async prompt(text, options) {
|
|
36
|
+
+ setDocumentResolver(resolve) {
|
|
37
|
+
+ this._documentResolver = resolve;
|
|
38
|
+
+ this._extensionRunner.resolveDocuments = (messages) => resolveDocumentMessages(messages,
|
|
39
|
+
+ this._activeCompactionModel ?? this.model, { resolve: this._documentResolver,
|
|
40
|
+
+ sessionId: this.sessionManager.getSessionId(),
|
|
41
|
+
+ inputId: this.messages.findLast(message => message.role === "user")?.clientInputId,
|
|
42
|
+
+ signal: this._compactionAbortController?.signal });
|
|
43
|
+
+ }
|
|
44
|
+
+ async prompt(text, options) {
|
|
45
|
+
+ if (options?.documents !== undefined) {
|
|
46
|
+
+ const normalized = normalizeDocumentInput(options, options.documents.every(document => document.source !== undefined));
|
|
47
|
+
+ options = { ...options, ...normalized, expandPromptTemplates: false };
|
|
48
|
+
+ }
|
|
49
|
+
@@ -4272,1 +4289,2 @@
|
|
50
|
+
- ...((options?.expandPromptTemplates ?? true) ? { prepare: true } : {}),
|
|
51
|
+
+ documents: options?.documents, attachments: options?.attachments, clientInputId: options?.clientInputId,
|
|
52
|
+
+ ...((options?.expandPromptTemplates ?? true) ? { prepare: true } : {}),
|
|
53
|
+
@@ -4280,2 +4298,3 @@
|
|
54
|
+
- inputId: options.clientMessageId ?? `prompt-${crypto.randomUUID()}`,
|
|
55
|
+
- revision: 0,
|
|
56
|
+
+ inputId: options.clientMessageId ?? `prompt-${crypto.randomUUID()}`,
|
|
57
|
+
+ documents: options.documents, attachments: options.attachments, clientInputId: options.clientInputId,
|
|
58
|
+
+ revision: 0,
|
|
59
|
+
@@ -4478,1 +4497,1 @@
|
|
60
|
+
- hasImages: (options?.images?.length ?? 0) > 0,
|
|
61
|
+
+ hasImages: (options?.images?.length ?? 0) > 0 || (options?.documents?.length ?? 0) > 0,
|
|
62
|
+
@@ -4712,3 +4731,5 @@
|
|
63
|
+
- const userMessage = {
|
|
64
|
+
- role: "user",
|
|
65
|
+
- content: userContent,
|
|
66
|
+
+ userContent.push(...(options?.documents ?? []));
|
|
67
|
+
+ const userMessage = {
|
|
68
|
+
+ ...documentInputMetadata(options),
|
|
69
|
+
+ role: "user",
|
|
70
|
+
+ content: userContent,
|
|
71
|
+
@@ -5013,1 +5034,4 @@
|
|
72
|
+
- async _admitQueuedInput(text, images, behavior, options) {
|
|
73
|
+
+ async _admitQueuedInput(text, images, behavior, options) {
|
|
74
|
+
+ if (options?.documents !== undefined) {
|
|
75
|
+
+ options = { ...options, ...normalizeDocumentInput(options, options.documents.every(document => document.source !== undefined)), expandPromptTemplates: false };
|
|
76
|
+
+ }
|
|
77
|
+
@@ -5166,1 +5190,1 @@
|
|
78
|
+
- await this._pendingInputs.admit({ clientMessageId, text: input.text, ...(input.images ? { images: input.images } : {}), mode: input.mode }, (queuedId) => this._enqueuePreparedInput({ ...sourced, queuedId }));
|
|
79
|
+
+ await this._pendingInputs.admit({ clientMessageId, text: input.text, ...(input.images ? { images: input.images } : {}), documents: input.documents, attachments: input.attachments, mode: input.mode }, (queuedId) => this._enqueuePreparedInput({ ...sourced, queuedId }));
|
|
80
|
+
@@ -5183,2 +5207,4 @@
|
|
81
|
+
- images: input.images,
|
|
82
|
+
- mode: input.mode,
|
|
83
|
+
+ images: input.images,
|
|
84
|
+
+ documents: input.documents,
|
|
85
|
+
+ attachments: input.attachments,
|
|
86
|
+
+ mode: input.mode,
|
|
87
|
+
@@ -5200,4 +5226,6 @@
|
|
88
|
+
- ...(input.images ?? []),
|
|
89
|
+
- ];
|
|
90
|
+
- const message = {
|
|
91
|
+
- role: "user",
|
|
92
|
+
+ ...(input.images ?? []),
|
|
93
|
+
+ ...(input.documents ?? []),
|
|
94
|
+
+ ];
|
|
95
|
+
+ const message = {
|
|
96
|
+
+ ...documentInputMetadata(input),
|
|
97
|
+
+ role: "user",
|
|
98
|
+
@@ -6015,2 +6043,2 @@
|
|
99
|
+
- const hasImages = (input.images?.length ?? 0) > 0;
|
|
100
|
+
- if (this._isExtensionCommandText(input.text))
|
|
101
|
+
+ const hasImages = (input.images?.length ?? 0) > 0 || (input.documents?.length ?? 0) > 0;
|
|
102
|
+
+ if (!input.documents?.length && this._isExtensionCommandText(input.text))
|
|
103
|
+
@@ -6046,3 +6074,4 @@
|
|
104
|
+
- role: "custom",
|
|
105
|
+
- customType: HELD_INPUT_ENTRY,
|
|
106
|
+
- content: input.images === undefined ? text : [{ type: "text", text }, ...input.images],
|
|
107
|
+
+ role: input.documents?.length ? "user" : "custom",
|
|
108
|
+
+ ...documentInputMetadata(input), ...clientMessageIdentity(input),
|
|
109
|
+
+ customType: HELD_INPUT_ENTRY,
|
|
110
|
+
+ content: [{ type: "text", text }, ...(input.images ?? []), ...(input.documents ?? [])],
|
|
111
|
+
@@ -9634,1 +9663,2 @@
|
|
112
|
+
setThinkingLevel: (level) => this.setThinkingLevel(level),
|
|
113
|
+
+ setDocumentResolver: (resolve) => this.setDocumentResolver(resolve),
|
|
114
|
+
--- a/@code-yeongyu/senpi/dist/core/compaction/compaction.js
|
|
115
|
+
+++ b/@code-yeongyu/senpi/dist/core/compaction/compaction.js
|
|
116
|
+
@@ -289,3 +289,6 @@
|
|
117
|
+
- else if (block.type === "image") {
|
|
118
|
+
- chars += ESTIMATED_IMAGE_CHARS;
|
|
119
|
+
- }
|
|
120
|
+
+ else if (block.type === "image") {
|
|
121
|
+
+ chars += ESTIMATED_IMAGE_CHARS;
|
|
122
|
+
+ }
|
|
123
|
+
+ else if (block.type === "document") {
|
|
124
|
+
+ chars += block.data ? block.data.length * 4 : JSON.stringify(block).length + ESTIMATED_IMAGE_CHARS;
|
|
125
|
+
+ }
|
|
126
|
+
--- a/@code-yeongyu/senpi/dist/core/extensions/builtin/anthropic-subscription/cold-seed-images.js
|
|
127
|
+
+++ b/@code-yeongyu/senpi/dist/core/extensions/builtin/anthropic-subscription/cold-seed-images.js
|
|
128
|
+
@@ -63,1 +63,1 @@
|
|
129
|
+
- if (!isRecord(entry) || entry.type !== "image")
|
|
130
|
+
+ if (!isRecord(entry) || entry.type !== "image" || entry.documentSource !== undefined)
|
|
131
|
+
--- a/@code-yeongyu/senpi/dist/core/extensions/builtin/anthropic-subscription/stream.js
|
|
132
|
+
+++ b/@code-yeongyu/senpi/dist/core/extensions/builtin/anthropic-subscription/stream.js
|
|
133
|
+
@@ -1,1 +1,2 @@
|
|
134
|
+
-import { createAssistantMessageDiagnostic, createAssistantMessageEventStream, } from "@earendil-works/pi-ai";
|
|
135
|
+
+import { assertDocumentRequestBudget, hasDocuments, DocumentInputError } from "@earendil-works/pi-ai";
|
|
136
|
+
+import { createAssistantMessageDiagnostic, createAssistantMessageEventStream, } from "@earendil-works/pi-ai";
|
|
137
|
+
@@ -79,1 +80,4 @@
|
|
138
|
+
- let oneShotCacheTtl;
|
|
139
|
+
+ let oneShotCacheTtl;
|
|
140
|
+
+ let documentPromptBlocks;
|
|
141
|
+
+ let documentQueryStarted = false;
|
|
142
|
+
+ const carriesDocuments = hasDocuments(context.messages);
|
|
143
|
+
@@ -105,1 +109,7 @@
|
|
144
|
+
- return replacement === undefined ? queryOptions : replacement;
|
|
145
|
+
+ const preparedOptions = replacement === undefined ? queryOptions : replacement;
|
|
146
|
+
+ if (carriesDocuments) {
|
|
147
|
+
+ documentPromptBlocks = dedupeUltraworkBlocks(buildPromptBlocks(context, resolvedTools.customToolNameToSdk,
|
|
148
|
+
+ toolWatchNote, oneShotCacheTtl === undefined ? {} : { cacheBreakpoint: oneShotCacheTtl })).blocks;
|
|
149
|
+
+ assertDocumentRequestBudget({ options: preparedOptions, prompt: documentPromptBlocks }, model, context.messages);
|
|
150
|
+
+ }
|
|
151
|
+
+ return preparedOptions;
|
|
152
|
+
@@ -118,1 +128,1 @@
|
|
153
|
+
- const useResidentSession = options?.streamKind === "main" && providerSettings.resumeMode !== "off" && options.sessionId !== undefined;
|
|
154
|
+
+ const useResidentSession = !carriesDocuments && options?.streamKind === "main" && providerSettings.resumeMode !== "off" && options.sessionId !== undefined;
|
|
155
|
+
@@ -154,2 +164,6 @@
|
|
156
|
+
- prompt: buildDeferredPromptStream(() => dedupeUltraworkBlocks(buildPromptBlocks(context, resolvedTools.customToolNameToSdk, toolWatchNote, oneShotCacheTtl === undefined ? {} : { cacheBreakpoint: oneShotCacheTtl })).blocks),
|
|
157
|
+
- query: getSdkBoundary().query,
|
|
158
|
+
+ prompt: buildDeferredPromptStream(() => documentPromptBlocks ?? dedupeUltraworkBlocks(buildPromptBlocks(context, resolvedTools.customToolNameToSdk, toolWatchNote, oneShotCacheTtl === undefined ? {} : { cacheBreakpoint: oneShotCacheTtl })).blocks),
|
|
159
|
+
+ query: (request) => {
|
|
160
|
+
+ if (carriesDocuments && documentQueryStarted) throw new DocumentInputError("attachment_delivery_uncertain");
|
|
161
|
+
+ documentQueryStarted = true;
|
|
162
|
+
+ return getSdkBoundary().query(request);
|
|
163
|
+
+ },
|
|
164
|
+
@@ -269,1 +283,1 @@
|
|
165
|
+
- output.errorMessage = withAuthGuidance(error, errorMessage(error), claudeCodeRun);
|
|
166
|
+
+ output.errorMessage = (hasDocuments(context.messages) ? "senpi:no-turn-retry:" : "") + withAuthGuidance(error, errorMessage(error), claudeCodeRun);
|
|
167
|
+
--- a/@code-yeongyu/senpi/dist/core/extensions/builtin/compaction/openai-remote-convert.js
|
|
168
|
+
+++ b/@code-yeongyu/senpi/dist/core/extensions/builtin/compaction/openai-remote-convert.js
|
|
169
|
+
@@ -1,1 +1,2 @@
|
|
170
|
+
-import { protectedRemoteInput } from "./task-intent.js";
|
|
171
|
+
+import { checkedDocument, DocumentInputError } from "@earendil-works/pi-ai";
|
|
172
|
+
+import { protectedRemoteInput } from "./task-intent.js";
|
|
173
|
+
@@ -35,2 +36,8 @@
|
|
174
|
+
- return {
|
|
175
|
+
- type: "input_image",
|
|
176
|
+
+ if (block.type === "document") {
|
|
177
|
+
+ if (block.source) throw new DocumentInputError("attachment_unresolved_reference");
|
|
178
|
+
+ const document = checkedDocument({ type: block.type, name: block.name, mimeType: block.mimeType,
|
|
179
|
+
+ data: block.data, provenance: block.provenance });
|
|
180
|
+
+ return { type: "input_file", filename: document.name, file_data: `data:application/pdf;base64,${document.data}` };
|
|
181
|
+
+ }
|
|
182
|
+
+ return {
|
|
183
|
+
+ type: "input_image",
|
|
184
|
+
--- a/@code-yeongyu/senpi/dist/core/extensions/builtin/compaction/retained-message-safety.js
|
|
185
|
+
+++ b/@code-yeongyu/senpi/dist/core/extensions/builtin/compaction/retained-message-safety.js
|
|
186
|
+
@@ -1,1 +1,2 @@
|
|
187
|
+
-function isRecord(value) {
|
|
188
|
+
+import { documentReferenceSchema } from "@earendil-works/pi-ai";
|
|
189
|
+
+function isRecord(value) {
|
|
190
|
+
@@ -147,1 +148,4 @@
|
|
191
|
+
- if (!isFiniteNumber(value.timestamp) || !hasTextOnlyContent(value.content, true))
|
|
192
|
+
+ if (!isFiniteNumber(value.timestamp) || !(hasTextOnlyContent(value.content, true) ||
|
|
193
|
+
+ (value.origin === "owner-attachment" && typeof value.clientInputId === "string" &&
|
|
194
|
+
+ Array.isArray(value.content) && value.content.every(block =>
|
|
195
|
+
+ (block.type === "text" && typeof block.text === "string") || documentReferenceSchema.safeParse(block).success))))
|
|
196
|
+
--- a/@code-yeongyu/senpi/dist/core/extensions/runner.js
|
|
197
|
+
+++ b/@code-yeongyu/senpi/dist/core/extensions/runner.js
|
|
198
|
+
@@ -386,1 +386,5 @@
|
|
199
|
+
this.runtime.setThinkingLevel = actions.setThinkingLevel;
|
|
200
|
+
+ const pendingDocumentResolver = this.runtime.pendingDocumentResolver;
|
|
201
|
+
+ this.runtime.setDocumentResolver = actions.setDocumentResolver;
|
|
202
|
+
+ if (pendingDocumentResolver !== undefined)
|
|
203
|
+
+ actions.setDocumentResolver(pendingDocumentResolver);
|
|
204
|
+
@@ -1876,1 +1880,2 @@
|
|
205
|
+
- const transformedMessages = await this.emitContext(messages, excludeExtensionPath, "projection");
|
|
206
|
+
+ const projectedMessages = await this.emitContext(messages, excludeExtensionPath, "projection");
|
|
207
|
+
+ const transformedMessages = this.resolveDocuments ? await this.resolveDocuments(projectedMessages) : projectedMessages;
|
|
208
|
+
--- a/@code-yeongyu/senpi/dist/core/extensions/loader.js
|
|
209
|
+
+++ b/@code-yeongyu/senpi/dist/core/extensions/loader.js
|
|
210
|
+
@@ -199,1 +199,2 @@
|
|
211
|
+
setThinkingLevel: notInitialized,
|
|
212
|
+
+ setDocumentResolver: (resolve) => { runtime.pendingDocumentResolver = resolve; },
|
|
213
|
+
@@ -604,1 +605,10 @@
|
|
214
|
+
},
|
|
215
|
+
+ setDocumentResolver(resolve) {
|
|
216
|
+
+ assertActive();
|
|
217
|
+
+ if (resolve !== undefined && typeof resolve !== "function")
|
|
218
|
+
+ throw new TypeError("document_resolver_invalid");
|
|
219
|
+
+ if (runtime.documentResolverOwner !== undefined && runtime.documentResolverOwner !== extension.path)
|
|
220
|
+
+ throw new Error("document_resolver_already_registered");
|
|
221
|
+
+ runtime.documentResolverOwner = resolve === undefined ? undefined : extension.path;
|
|
222
|
+
+ applyRuntimeChange(() => runtime.setDocumentResolver(resolve === undefined ? undefined : ownedBy(extension, resolve)));
|
|
223
|
+
+ },
|
|
224
|
+
--- a/@code-yeongyu/senpi/dist/core/extensions/types.d.ts
|
|
225
|
+
+++ b/@code-yeongyu/senpi/dist/core/extensions/types.d.ts
|
|
226
|
+
@@ -2222,1 +2222,6 @@
|
|
227
|
+
setThinkingLevel(level: ThinkingLevel): void;
|
|
228
|
+
+ /**
|
|
229
|
+
+ * Register this session's grant-bound document resolver. The engine calls it at provider request time.
|
|
230
|
+
+ * One extension owns it per session; another extension gets document_resolver_already_registered.
|
|
231
|
+
+ */
|
|
232
|
+
+ setDocumentResolver(resolve: import("@earendil-works/pi-ai").DocumentResolver | undefined): void;
|
|
233
|
+
--- a/@code-yeongyu/senpi/dist/core/held-inputs.js
|
|
234
|
+
+++ b/@code-yeongyu/senpi/dist/core/held-inputs.js
|
|
235
|
+
@@ -1,1 +1,2 @@
|
|
236
|
+
-export const HELD_INPUT_ENTRY = "held-input";
|
|
237
|
+
+import { documentReferenceSchema, documentAttachmentSchema } from "@earendil-works/pi-ai";
|
|
238
|
+
+export const HELD_INPUT_ENTRY = "held-input";
|
|
239
|
+
@@ -23,1 +24,3 @@
|
|
240
|
+
- (data.prepare === undefined || data.prepare === true) &&
|
|
241
|
+
+ (data.prepare === undefined || data.prepare === true) &&
|
|
242
|
+
+ (data.documents === undefined || (Array.isArray(data.documents) && data.documents.every(value => documentReferenceSchema.safeParse(value).success))) &&
|
|
243
|
+
+ (data.attachments === undefined || (Array.isArray(data.attachments) && data.attachments.every(value => documentAttachmentSchema.safeParse(value).success))) &&
|
|
244
|
+
--- a/@code-yeongyu/senpi/dist/core/messages.js
|
|
245
|
+
+++ b/@code-yeongyu/senpi/dist/core/messages.js
|
|
246
|
+
@@ -222,2 +222,2 @@
|
|
247
|
+
- if (block.type !== "image")
|
|
248
|
+
- return;
|
|
249
|
+
+ if (block.type !== "image" || block.documentSource !== undefined)
|
|
250
|
+
+ return;
|
|
251
|
+
--- a/@code-yeongyu/senpi/dist/core/pending-input-session.js
|
|
252
|
+
+++ b/@code-yeongyu/senpi/dist/core/pending-input-session.js
|
|
253
|
+
@@ -193,1 +193,1 @@
|
|
254
|
+
- return sha256(JSON.stringify([input.mode, input.text, images]));
|
|
255
|
+
+ return sha256(JSON.stringify([input.mode, input.text, images, input.documents, input.attachments]));
|
|
256
|
+
--- a/@code-yeongyu/senpi/dist/core/session-manager.d.ts
|
|
257
|
+
+++ b/@code-yeongyu/senpi/dist/core/session-manager.d.ts
|
|
258
|
+
@@ -147,1 +147,3 @@
|
|
259
|
+
-export interface CompactionEntry<T = unknown> extends SessionEntryBase {
|
|
260
|
+
+export interface CompactionEntry<T = unknown> extends SessionEntryBase {
|
|
261
|
+
+ /** Grant references from summarized owner inputs, never inline document bytes. */
|
|
262
|
+
+ documentHistory?: UserMessage[];
|
|
263
|
+
--- a/@code-yeongyu/senpi/dist/core/session-manager.js
|
|
264
|
+
+++ b/@code-yeongyu/senpi/dist/core/session-manager.js
|
|
265
|
+
@@ -387,1 +387,2 @@
|
|
266
|
+
- return entry.systemMessage ? [withContextEntryId(entry.id, entry.systemMessage), summary] : [summary];
|
|
267
|
+
+ const documents = (entry.documentHistory ?? []).map(message => withContextEntryId(entry.id, message));
|
|
268
|
+
+ return entry.systemMessage ? [withContextEntryId(entry.id, entry.systemMessage), summary, ...documents] : [summary, ...documents];
|
|
269
|
+
@@ -1493,2 +1494,16 @@
|
|
270
|
+
- appendCompaction(summary, firstKeptEntryId, tokensBefore, details, fromHook, usage) {
|
|
271
|
+
- const timestamp = new Date().toISOString();
|
|
272
|
+
+ appendCompaction(summary, firstKeptEntryId, tokensBefore, details, fromHook, usage) {
|
|
273
|
+
+ const branch = this.getBranch();
|
|
274
|
+
+ const firstKept = branch.findIndex(entry => entry.id === firstKeptEntryId);
|
|
275
|
+
+ const documentHistory = new Map();
|
|
276
|
+
+ for (const entry of branch.slice(0, firstKept < 0 ? branch.length : firstKept)) {
|
|
277
|
+
+ for (const message of sessionEntryToContextMessages(entry)) {
|
|
278
|
+
+ if (message.role !== "user" || message.origin !== "owner-attachment" || !Array.isArray(message.content)) continue;
|
|
279
|
+
+ const content = message.content.filter(block => block.type === "document");
|
|
280
|
+
+ if (content.length) documentHistory.set(message.clientInputId, {
|
|
281
|
+
+ role: "user", origin: "owner-attachment", clientInputId: message.clientInputId,
|
|
282
|
+
+ clientMessageId: message.clientMessageId, clientTurnId: message.clientTurnId,
|
|
283
|
+
+ attachments: message.attachments, content, timestamp: message.timestamp,
|
|
284
|
+
+ });
|
|
285
|
+
+ }
|
|
286
|
+
+ }
|
|
287
|
+
+ const timestamp = new Date().toISOString();
|
|
288
|
+
@@ -1498,2 +1513,3 @@
|
|
289
|
+
- type: "compaction",
|
|
290
|
+
- id,
|
|
291
|
+
+ type: "compaction",
|
|
292
|
+
+ ...(documentHistory.size ? { documentHistory: [...documentHistory.values()] } : {}),
|
|
293
|
+
+ id,
|
|
294
|
+
--- a/@code-yeongyu/senpi/dist/modes/rpc/client-admission-record.js
|
|
295
|
+
+++ b/@code-yeongyu/senpi/dist/modes/rpc/client-admission-record.js
|
|
296
|
+
@@ -1,2 +1,3 @@
|
|
297
|
+
-import { createHash } from "node:crypto";
|
|
298
|
+
-import { z } from "zod";
|
|
299
|
+
+import { documentReferenceSchema, documentAttachmentSchema } from "@earendil-works/pi-ai";
|
|
300
|
+
+import { createHash } from "node:crypto";
|
|
301
|
+
+import { z } from "zod";
|
|
302
|
+
@@ -30,1 +31,3 @@
|
|
303
|
+
- mode: z.enum(["steer", "followUp"]),
|
|
304
|
+
+ documents: z.array(documentReferenceSchema).max(4).optional(),
|
|
305
|
+
+ attachments: z.array(documentAttachmentSchema).max(4).optional(),
|
|
306
|
+
+ mode: z.enum(["steer", "followUp"]),
|
|
307
|
+
@@ -62,1 +65,3 @@
|
|
308
|
+
- clientTurnId: command.clientTurnId,
|
|
309
|
+
+ clientTurnId: command.clientTurnId,
|
|
310
|
+
+ documents: command.documents,
|
|
311
|
+
+ attachments: command.attachments,
|
|
312
|
+
--- a/@code-yeongyu/senpi/dist/modes/rpc/client-input-handler.js
|
|
313
|
+
+++ b/@code-yeongyu/senpi/dist/modes/rpc/client-input-handler.js
|
|
314
|
+
@@ -1,2 +1,3 @@
|
|
315
|
+
-import { AgentBusyError } from "../../core/agent-busy-error.js";
|
|
316
|
+
-import { clientMessageIdentity } from "../../core/client-message-identity.js";
|
|
317
|
+
+import { normalizeDocumentInput } from "@earendil-works/pi-ai";
|
|
318
|
+
+import { AgentBusyError } from "../../core/agent-busy-error.js";
|
|
319
|
+
+import { clientMessageIdentity } from "../../core/client-message-identity.js";
|
|
320
|
+
@@ -11,1 +12,1 @@
|
|
321
|
+
- command = { ...command, ...clientMessageIdentity(command) };
|
|
322
|
+
+ command = { ...command, ...clientMessageIdentity(command), ...normalizeDocumentInput(command) };
|
|
323
|
+
@@ -43,1 +44,3 @@
|
|
324
|
+
- images: command.images,
|
|
325
|
+
+ images: command.images,
|
|
326
|
+
+ documents: command.documents,
|
|
327
|
+
+ attachments: command.attachments,
|
|
328
|
+
@@ -92,2 +95,4 @@
|
|
329
|
+
- enqueueOrder: command.enqueueOrder,
|
|
330
|
+
- source: "rpc",
|
|
331
|
+
+ enqueueOrder: command.enqueueOrder,
|
|
332
|
+
+ documents: command.documents,
|
|
333
|
+
+ attachments: command.attachments,
|
|
334
|
+
+ source: "rpc",
|
|
335
|
+
--- a/@code-yeongyu/senpi/dist/modes/rpc/connection-handler.js
|
|
336
|
+
+++ b/@code-yeongyu/senpi/dist/modes/rpc/connection-handler.js
|
|
337
|
+
@@ -899,1 +899,2 @@
|
|
338
|
+
- "multi_session",
|
|
339
|
+
+ "multi_session",
|
|
340
|
+
+ "document_content_v1",
|
|
341
|
+
--- a/@code-yeongyu/senpi/dist/modes/rpc/host-capabilities.js
|
|
342
|
+
+++ b/@code-yeongyu/senpi/dist/modes/rpc/host-capabilities.js
|
|
343
|
+
@@ -23,1 +23,2 @@
|
|
344
|
+
"snapshot_cursor",
|
|
345
|
+
+ "document_content_v1",
|
|
346
|
+
--- a/@code-yeongyu/senpi/dist/modes/rpc/rpc-input-validation.js
|
|
347
|
+
+++ b/@code-yeongyu/senpi/dist/modes/rpc/rpc-input-validation.js
|
|
348
|
+
@@ -1,2 +1,3 @@
|
|
349
|
+
-import { isBrowserEngine } from "../../core/browser-engine.js";
|
|
350
|
+
-import { clientMessageIdentitySchema } from "../../core/client-message-identity.js";
|
|
351
|
+
+import { normalizeDocumentInput, documentReferenceSchema } from "@earendil-works/pi-ai";
|
|
352
|
+
+import { isBrowserEngine } from "../../core/browser-engine.js";
|
|
353
|
+
+import { clientMessageIdentitySchema } from "../../core/client-message-identity.js";
|
|
354
|
+
@@ -17,1 +18,1 @@
|
|
355
|
+
-function validContent(content) {
|
|
356
|
+
+function validContent(content, allowDocuments = false) {
|
|
357
|
+
@@ -27,1 +28,3 @@
|
|
358
|
+
- : value.type === "image" && typeof value.data === "string" && typeof value.mimeType === "string";
|
|
359
|
+
+ : value.type === "document"
|
|
360
|
+
+ ? allowDocuments && documentReferenceSchema.safeParse(value).success
|
|
361
|
+
+ : value.type === "image" && typeof value.data === "string" && typeof value.mimeType === "string";
|
|
362
|
+
@@ -47,1 +50,8 @@
|
|
363
|
+
- return ["user", "assistant", "tool"].includes(item.role) && validContent(item.content);
|
|
364
|
+
+ if (!["user", "assistant", "tool"].includes(item.role) || !validContent(item.content, item.role === "user")) return false;
|
|
365
|
+
+ const documents = Array.isArray(item.content) ? item.content.filter(block => block.type === "document") : [];
|
|
366
|
+
+ if (documents.length) {
|
|
367
|
+
+ if (item.origin !== "owner-attachment" || !clientMessageIdentitySchema.safeParse(item).success) return false;
|
|
368
|
+
+ try { normalizeDocumentInput({ clientInputId: item.clientInputId, documents, attachments: item.attachments }, true); }
|
|
369
|
+
+ catch (error) { if (error instanceof Error) return false; throw error; }
|
|
370
|
+
+ }
|
|
371
|
+
+ return true;
|
|
372
|
+
@@ -76,2 +86,7 @@
|
|
373
|
+
- const value = command;
|
|
374
|
+
- if ((value.type === "prompt" || value.type === "steer" || value.type === "follow_up") &&
|
|
375
|
+
+ const value = command;
|
|
376
|
+
+ if (["prompt", "steer", "follow_up"].includes(value.type) &&
|
|
377
|
+
+ (value.documents !== undefined || value.attachments !== undefined)) {
|
|
378
|
+
+ try { normalizeDocumentInput(value); }
|
|
379
|
+
+ catch (error) { return error instanceof Error ? error.message : "attachment_document_invalid"; }
|
|
380
|
+
+ }
|
|
381
|
+
+ if ((value.type === "prompt" || value.type === "steer" || value.type === "follow_up") &&
|
|
382
|
+
--- a/@code-yeongyu/senpi/dist/modes/rpc/rpc-session-command-types.d.ts
|
|
383
|
+
+++ b/@code-yeongyu/senpi/dist/modes/rpc/rpc-session-command-types.d.ts
|
|
384
|
+
@@ -2,1 +2,1 @@
|
|
385
|
+
-import type { ImageContent } from "@earendil-works/pi-ai";
|
|
386
|
+
+import type { ImageContent, DocumentContent, DocumentAttachment } from "@earendil-works/pi-ai";
|
|
387
|
+
@@ -13,1 +13,3 @@
|
|
388
|
+
- images?: ImageContent[];
|
|
389
|
+
+ images?: ImageContent[];
|
|
390
|
+
+ documents?: DocumentContent[];
|
|
391
|
+
+ attachments?: DocumentAttachment[];
|
|
392
|
+
@@ -63,1 +65,3 @@
|
|
393
|
+
- images?: ImageContent[];
|
|
394
|
+
+ images?: ImageContent[];
|
|
395
|
+
+ documents?: DocumentContent[];
|
|
396
|
+
+ attachments?: DocumentAttachment[];
|
|
397
|
+
@@ -71,1 +75,3 @@
|
|
398
|
+
- images?: ImageContent[];
|
|
399
|
+
+ images?: ImageContent[];
|
|
400
|
+
+ documents?: DocumentContent[];
|
|
401
|
+
+ attachments?: DocumentAttachment[];
|
|
402
|
+
--- a/@earendil-works/pi-ai/dist/api/anthropic-messages.js
|
|
403
|
+
+++ b/@earendil-works/pi-ai/dist/api/anthropic-messages.js
|
|
404
|
+
@@ -1,1 +1,2 @@
|
|
405
|
+
-import Anthropic from "@anthropic-ai/sdk";
|
|
406
|
+
+import { checkedDocument, documentReadMode, DocumentInputError, assertDocumentRequestBudget, hasDocuments } from "../types.js";
|
|
407
|
+
+import Anthropic from "@anthropic-ai/sdk";
|
|
408
|
+
@@ -1055,1 +1056,2 @@
|
|
409
|
+
- logAnthropicRequestShape(model, body);
|
|
410
|
+
+ assertDocumentRequestBudget({ ...body, stream: true }, model, normalizedContext.messages);
|
|
411
|
+
+ logAnthropicRequestShape(model, body);
|
|
412
|
+
@@ -1123,1 +1125,1 @@
|
|
413
|
+
- maxRetries: options?.maxRetries,
|
|
414
|
+
+ maxRetries: hasDocuments(normalizedContext.messages) ? 0 : options?.maxRetries,
|
|
415
|
+
@@ -1492,1 +1494,1 @@
|
|
416
|
+
- output.errorMessage = withGitHubCopilotFailureNote(formatGitHubCopilotToolLimitError(output, errorMessage), model.provider, error);
|
|
417
|
+
+ output.errorMessage = (hasDocuments(normalizedContext.messages) ? "senpi:no-turn-retry:" : "") + withGitHubCopilotFailureNote(formatGitHubCopilotToolLimitError(output, errorMessage), model.provider, error);
|
|
418
|
+
@@ -2158,1 +2160,6 @@
|
|
419
|
+
- else if (isVideoMimeType(item.mimeType)) {
|
|
420
|
+
+ else if (item.type === "document") {
|
|
421
|
+
+ if (documentReadMode(model) !== "native-document") throw new DocumentInputError("attachment_document_route");
|
|
422
|
+
+ const document = checkedDocument({ type: item.type, mimeType: item.mimeType, name: item.name, data: item.data, provenance: item.provenance });
|
|
423
|
+
+ return { type: "document", title: document.name, source: { type: "base64", media_type: "application/pdf", data: document.data } };
|
|
424
|
+
+ }
|
|
425
|
+
+ else if (isVideoMimeType(item.mimeType)) {
|
|
426
|
+
--- a/@earendil-works/pi-ai/dist/api/openai-codex-responses.js
|
|
427
|
+
+++ b/@earendil-works/pi-ai/dist/api/openai-codex-responses.js
|
|
428
|
+
@@ -1,1 +1,2 @@
|
|
429
|
+
-import { readProviderDiagnostic } from "../provider-diagnostic.js";
|
|
430
|
+
+import { assertDocumentRequestBudget, hasDocuments } from "../types.js";
|
|
431
|
+
+import { readProviderDiagnostic } from "../provider-diagnostic.js";
|
|
432
|
+
@@ -183,1 +184,2 @@
|
|
433
|
+
- const bodyJson = JSON.stringify(body);
|
|
434
|
+
+ assertDocumentRequestBudget(body, model, normalizedContext.messages);
|
|
435
|
+
+ const bodyJson = JSON.stringify(body);
|
|
436
|
+
@@ -186,1 +188,1 @@
|
|
437
|
+
- const transport = options?.transport || "auto";
|
|
438
|
+
+ const transport = hasDocuments(normalizedContext.messages) ? "sse" : options?.transport || "auto";
|
|
439
|
+
@@ -262,1 +264,1 @@
|
|
440
|
+
- const maxRetries = options?.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
441
|
+
+ const maxRetries = hasDocuments(normalizedContext.messages) ? 0 : options?.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
442
|
+
@@ -369,1 +371,1 @@
|
|
443
|
+
- output.errorMessage = formatProviderError(normalizeProviderError(error));
|
|
444
|
+
+ output.errorMessage = (hasDocuments(normalizedContext.messages) ? "senpi:no-turn-retry:" : "") + formatProviderError(normalizeProviderError(error));
|
|
445
|
+
--- a/@earendil-works/pi-ai/dist/api/openai-responses-shared.js
|
|
446
|
+
+++ b/@earendil-works/pi-ai/dist/api/openai-responses-shared.js
|
|
447
|
+
@@ -1,1 +1,2 @@
|
|
448
|
+
-import { CONTEXT_PROVENANCE_FIELD, contextProvenanceFingerprint, getContextProvenance, } from "../context-provenance.js";
|
|
449
|
+
+import { checkedDocument, documentReadMode, DocumentInputError } from "../types.js";
|
|
450
|
+
+import { CONTEXT_PROVENANCE_FIELD, contextProvenanceFingerprint, getContextProvenance, } from "../context-provenance.js";
|
|
451
|
+
@@ -273,2 +274,7 @@
|
|
452
|
+
- return {
|
|
453
|
+
- type: "input_image",
|
|
454
|
+
+ if (item.type === "document") {
|
|
455
|
+
+ if (documentReadMode(model) !== "native-document") throw new DocumentInputError("attachment_document_route");
|
|
456
|
+
+ const document = checkedDocument({ type: item.type, mimeType: item.mimeType, name: item.name, data: item.data, provenance: item.provenance });
|
|
457
|
+
+ return { type: "input_file", filename: document.name, file_data: `data:application/pdf;base64,${document.data}` };
|
|
458
|
+
+ }
|
|
459
|
+
+ return {
|
|
460
|
+
+ type: "input_image",
|
|
461
|
+
--- a/@earendil-works/pi-ai/dist/api/openai-responses.js
|
|
462
|
+
+++ b/@earendil-works/pi-ai/dist/api/openai-responses.js
|
|
463
|
+
@@ -1,1 +1,2 @@
|
|
464
|
+
-import OpenAI from "openai";
|
|
465
|
+
+import { assertDocumentRequestBudget, hasDocuments } from "../types.js";
|
|
466
|
+
+import OpenAI from "openai";
|
|
467
|
+
@@ -203,1 +204,2 @@
|
|
468
|
+
- const transport = options?.transport ?? "sse";
|
|
469
|
+
+ assertDocumentRequestBudget(params, model, normalizedContext.messages);
|
|
470
|
+
+ const transport = hasDocuments(normalizedContext.messages) ? "sse" : options?.transport ?? "sse";
|
|
471
|
+
@@ -268,1 +270,1 @@
|
|
472
|
+
- maxRetries: options?.maxRetries,
|
|
473
|
+
+ maxRetries: hasDocuments(normalizedContext.messages) ? 0 : options?.maxRetries,
|
|
474
|
+
@@ -307,1 +309,1 @@
|
|
475
|
+
- output.errorMessage = withGitHubCopilotFailureNote(formatGitHubCopilotToolLimitError(output, formatOpenAIResponsesError(error, model.provider)), model.provider, error);
|
|
476
|
+
+ output.errorMessage = (hasDocuments(normalizedContext.messages) ? "senpi:no-turn-retry:" : "") + withGitHubCopilotFailureNote(formatGitHubCopilotToolLimitError(output, formatOpenAIResponsesError(error, model.provider)), model.provider, error);
|
|
477
|
+
--- a/@earendil-works/pi-ai/dist/types.d.ts
|
|
478
|
+
+++ b/@earendil-works/pi-ai/dist/types.d.ts
|
|
479
|
+
@@ -521,4 +521,6 @@
|
|
480
|
+
- content: string | (TextContent | ImageContent)[];
|
|
481
|
+
- timestamp: number;
|
|
482
|
+
-}
|
|
483
|
+
-/** A durable Responses configuration change projected into the message stream. */
|
|
484
|
+
+ content: string | (TextContent | ImageContent | DocumentContent | DocumentReference)[];
|
|
485
|
+
+ origin?: "owner-attachment";
|
|
486
|
+
+ attachments?: DocumentAttachment[];
|
|
487
|
+
+ timestamp: number;
|
|
488
|
+
+}
|
|
489
|
+
+/** A durable Responses configuration change projected into the message stream. */
|
|
490
|
+
@@ -1254,1 +1256,93 @@
|
|
491
|
+
-//# sourceMappingURL=types.d.ts.map
|
|
492
|
+
+//# sourceMappingURL=types.d.ts.map
|
|
493
|
+
+/** UNRELEASED declarations appended to pi-ai/dist/types.d.ts, in its type scope. */
|
|
494
|
+
+export interface DocumentProvenance {
|
|
495
|
+
+ readonly attachmentId: string;
|
|
496
|
+
+ readonly grantId: string;
|
|
497
|
+
+ readonly originalSha256: string;
|
|
498
|
+
+}
|
|
499
|
+
+export interface DocumentContent {
|
|
500
|
+
+ readonly type: "document";
|
|
501
|
+
+ readonly mimeType: "application/pdf";
|
|
502
|
+
+ readonly name: string;
|
|
503
|
+
+ readonly data: string;
|
|
504
|
+
+ readonly provenance: DocumentProvenance;
|
|
505
|
+
+}
|
|
506
|
+
+export interface DocumentReference {
|
|
507
|
+
+ readonly type: "document";
|
|
508
|
+
+ readonly mimeType: "application/pdf";
|
|
509
|
+
+ readonly name: string;
|
|
510
|
+
+ readonly source: DocumentProvenance & { readonly kind: "attachment" };
|
|
511
|
+
+}
|
|
512
|
+
+export interface DocumentAttachment {
|
|
513
|
+
+ readonly id: string;
|
|
514
|
+
+ readonly grantId: string;
|
|
515
|
+
+ readonly name: string;
|
|
516
|
+
+ readonly format: "html" | "md" | "txt" | "pdf";
|
|
517
|
+
+ readonly bytes: number;
|
|
518
|
+
+ readonly sha256: string;
|
|
519
|
+
+ readonly readMode: "text" | "native-document" | "pages" | "ocr";
|
|
520
|
+
+ readonly pageCount?: number;
|
|
521
|
+
+}
|
|
522
|
+
+export interface DocumentCoverage {
|
|
523
|
+
+ readonly totalPages: number;
|
|
524
|
+
+ readonly pages: readonly {
|
|
525
|
+
+ readonly page: number;
|
|
526
|
+
+ readonly representation: "native-text" | "rendered-image" | "ocr-text" | "verified-blank";
|
|
527
|
+
+ }[];
|
|
528
|
+
+}
|
|
529
|
+
+export interface DocumentRepresentation {
|
|
530
|
+
+ readonly status: "ready";
|
|
531
|
+
+ readonly originalSha256: string;
|
|
532
|
+
+ readonly representationSha256: string;
|
|
533
|
+
+ /** Delivery content/coverage/cursor digest; separate from the reader's full representation digest. */
|
|
534
|
+
+ readonly payloadSha256: string;
|
|
535
|
+
+ readonly grantRevision: number;
|
|
536
|
+
+ readonly estimatedTokens: number;
|
|
537
|
+
+ readonly content: readonly (TextContent | ImageContent | DocumentContent)[];
|
|
538
|
+
+ readonly coverage: DocumentCoverage;
|
|
539
|
+
+ readonly nextCursor?: string;
|
|
540
|
+
+}
|
|
541
|
+
+/** The caller MUST authorize against the current managed binding and grant before releasing bytes. */
|
|
542
|
+
+export type DocumentResolver = (request: {
|
|
543
|
+
+ readonly reference: DocumentReference;
|
|
544
|
+
+ readonly mode: "native-document" | "pages" | "ocr";
|
|
545
|
+
+ readonly sessionId: string;
|
|
546
|
+
+ readonly inputId: string | undefined;
|
|
547
|
+
+ readonly originInputId: string;
|
|
548
|
+
+ readonly model: Model<Api>;
|
|
549
|
+
+ readonly signal: AbortSignal | undefined;
|
|
550
|
+
+}) => Promise<DocumentRepresentation | {
|
|
551
|
+
+ readonly status: "attachment_revoked" | "attachment_unavailable" | "attachment_text_held";
|
|
552
|
+
+}>;
|
|
553
|
+
+export declare class DocumentInputError extends Error {
|
|
554
|
+
+ readonly code: string;
|
|
555
|
+
+ constructor(code: string);
|
|
556
|
+
+}
|
|
557
|
+
+export declare const documentReferenceSchema: import("zod").ZodType<DocumentReference>;
|
|
558
|
+
+export declare const documentContentSchema: import("zod").ZodType<DocumentContent>;
|
|
559
|
+
+export declare const documentAttachmentSchema: import("zod").ZodType<DocumentAttachment>;
|
|
560
|
+
+export declare function checkedDocument(value: unknown): DocumentContent;
|
|
561
|
+
+export declare function normalizeDocumentInput(input: {
|
|
562
|
+
+ readonly clientInputId?: string;
|
|
563
|
+
+ readonly documents?: readonly unknown[];
|
|
564
|
+
+ readonly attachments?: readonly unknown[];
|
|
565
|
+
+}, references?: boolean): {
|
|
566
|
+
+ readonly documents?: DocumentReference[];
|
|
567
|
+
+ readonly attachments?: DocumentAttachment[];
|
|
568
|
+
+ readonly origin?: "owner-attachment";
|
|
569
|
+
+};
|
|
570
|
+
+export declare function documentInputMetadata(input?: {
|
|
571
|
+
+ readonly documents?: readonly unknown[];
|
|
572
|
+
+ readonly attachments?: readonly DocumentAttachment[];
|
|
573
|
+
+}): { readonly origin?: "owner-attachment"; readonly attachments?: readonly DocumentAttachment[] };
|
|
574
|
+
+export declare function documentReadMode(model: Model<Api>): "native-document" | "pages" | "ocr";
|
|
575
|
+
+export declare function hasDocuments(messages: readonly Message[]): boolean;
|
|
576
|
+
+export declare function documentRequestLimit(model: Model<Api>): number;
|
|
577
|
+
+export declare function assertDocumentRequestBudget(payload: unknown, model: Model<Api>, messages: readonly Message[]): void;
|
|
578
|
+
+export declare function resolveDocumentMessages(messages: readonly Message[], model: Model<Api>, boundary: {
|
|
579
|
+
+ readonly resolve?: DocumentResolver;
|
|
580
|
+
+ readonly sessionId: string;
|
|
581
|
+
+ readonly inputId?: string;
|
|
582
|
+
+ readonly signal?: AbortSignal;
|
|
583
|
+
+}): Promise<Message[]>;
|
|
584
|
+
+
|
|
585
|
+
--- a/@earendil-works/pi-ai/dist/types.js
|
|
586
|
+
+++ b/@earendil-works/pi-ai/dist/types.js
|
|
587
|
+
@@ -5,1 +5,235 @@
|
|
588
|
+
-//# sourceMappingURL=types.js.map
|
|
589
|
+
|
|
590
|
+
+//# sourceMappingURL=types.js.map
|
|
591
|
+
+// UNRELEASED T5 source appended to pi-ai/dist/types.js by the frozen source patch.
|
|
592
|
+
+// Gateway authorization is a required caller-owned boundary, not implemented here.
|
|
593
|
+
+import { createHash } from "node:crypto";
|
|
594
|
+
+import { z } from "zod";
|
|
595
|
+
+
|
|
596
|
+
+export class DocumentInputError extends Error {
|
|
597
|
+
+ constructor(code) {
|
|
598
|
+
+ super(code);
|
|
599
|
+
+ this.name = "DocumentInputError";
|
|
600
|
+
+ this.code = code;
|
|
601
|
+
+ }
|
|
602
|
+
+}
|
|
603
|
+
+const documentSha = z.string().regex(/^[a-f0-9]{64}$/);
|
|
604
|
+
+const documentSourceSchema = z.object({
|
|
605
|
+
+ attachmentId: z.string().uuid(), grantId: z.string().uuid(), originalSha256: documentSha,
|
|
606
|
+
+}).strict();
|
|
607
|
+
+const documentFields = {
|
|
608
|
+
+ type: z.literal("document"), mimeType: z.literal("application/pdf"), name: z.string().min(1).max(1024),
|
|
609
|
+
+};
|
|
610
|
+
+export const documentReferenceSchema = z.object({
|
|
611
|
+
+ ...documentFields, source: documentSourceSchema.extend({ kind: z.literal("attachment") }).strict(),
|
|
612
|
+
+}).strict();
|
|
613
|
+
+export const documentContentSchema = z.object({
|
|
614
|
+
+ ...documentFields, data: z.string().min(4).max(13981016)
|
|
615
|
+
+ .regex(/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/),
|
|
616
|
+
+ provenance: documentSourceSchema,
|
|
617
|
+
+}).strict();
|
|
618
|
+
+export const documentAttachmentSchema = z.object({
|
|
619
|
+
+ id: z.string().uuid(), grantId: z.string().uuid(), name: z.string().min(1).max(1024),
|
|
620
|
+
+ format: z.enum(["html", "md", "txt", "pdf"]), bytes: z.number().int().min(1).max(10485760),
|
|
621
|
+
+ sha256: documentSha, readMode: z.enum(["text", "native-document", "pages", "ocr"]),
|
|
622
|
+
+ pageCount: z.number().int().min(1).max(300).optional(),
|
|
623
|
+
+}).strict();
|
|
624
|
+
+export function checkedDocument(value) {
|
|
625
|
+
+ const parsed = documentContentSchema.safeParse(value);
|
|
626
|
+
+ if (!parsed.success) throw new DocumentInputError("attachment_document_invalid");
|
|
627
|
+
+ const document = parsed.data;
|
|
628
|
+
+ const bytes = Buffer.from(document.data, "base64");
|
|
629
|
+
+ if (bytes.length > 10485760 || bytes.toString("base64") !== document.data ||
|
|
630
|
+
+ bytes.subarray(0, 5).toString("ascii") !== "%PDF-" ||
|
|
631
|
+
+ createHash("sha256").update(bytes).digest("hex") !== document.provenance.originalSha256)
|
|
632
|
+
+ throw new DocumentInputError("attachment_document_integrity");
|
|
633
|
+
+ return document;
|
|
634
|
+
+}
|
|
635
|
+
+/** Ingress bytes are checked, then dropped. Durable records contain only sealed grant references. */
|
|
636
|
+
+export function normalizeDocumentInput(input, references = false) {
|
|
637
|
+
+ if (input.documents === undefined && input.attachments === undefined) return {};
|
|
638
|
+
+ if (!input.clientInputId || !Array.isArray(input.documents) ||
|
|
639
|
+
+ input.documents.length < 1 || input.documents.length > 4 ||
|
|
640
|
+
+ !Array.isArray(input.attachments) || input.attachments.length !== input.documents.length)
|
|
641
|
+
+ throw new DocumentInputError("attachment_document_identity");
|
|
642
|
+
+ const attachments = input.attachments.map(value => {
|
|
643
|
+
+ const result = documentAttachmentSchema.safeParse(value);
|
|
644
|
+
+ if (!result.success) throw new DocumentInputError("attachment_metadata_invalid");
|
|
645
|
+
+ return result.data;
|
|
646
|
+
+ });
|
|
647
|
+
+ const seen = new Set();
|
|
648
|
+
+ const documents = input.documents.map((value, index) => {
|
|
649
|
+
+ const document = references ? documentReferenceSchema.parse(value) : checkedDocument(value);
|
|
650
|
+
+ const source = references ? document.source : document.provenance;
|
|
651
|
+
+ const metadata = attachments[index];
|
|
652
|
+
+ if (seen.has(source.attachmentId) || metadata.id !== source.attachmentId ||
|
|
653
|
+
+ metadata.grantId !== source.grantId || metadata.sha256 !== source.originalSha256 ||
|
|
654
|
+
+ metadata.name !== document.name || metadata.format !== "pdf" ||
|
|
655
|
+
+ (!references && metadata.bytes !== Buffer.from(document.data, "base64").length))
|
|
656
|
+
+ throw new DocumentInputError("attachment_document_identity");
|
|
657
|
+
+ seen.add(source.attachmentId);
|
|
658
|
+
+ return { type: "document", mimeType: "application/pdf", name: document.name,
|
|
659
|
+
+ source: { kind: "attachment", attachmentId: source.attachmentId,
|
|
660
|
+
+ grantId: source.grantId, originalSha256: source.originalSha256 } };
|
|
661
|
+
+ });
|
|
662
|
+
+ return { documents, attachments, origin: "owner-attachment" };
|
|
663
|
+
+}
|
|
664
|
+
+export function documentInputMetadata(input) {
|
|
665
|
+
+ return input?.documents?.length
|
|
666
|
+
+ ? { origin: "owner-attachment", attachments: input.attachments }
|
|
667
|
+
+ : {};
|
|
668
|
+
+}
|
|
669
|
+
+/** Provider identity and API are both required; a lookalike model name grants no native-file route. */
|
|
670
|
+
+export function documentReadMode(model) {
|
|
671
|
+
+ if (!model?.input?.includes("image")) return "ocr";
|
|
672
|
+
+ if ((model.provider === "openai" && model.api === "openai-responses") ||
|
|
673
|
+
+ (model.provider === "anthropic" && model.api === "anthropic-messages")) return "native-document";
|
|
674
|
+
+ return "pages";
|
|
675
|
+
+}
|
|
676
|
+
+export function hasDocuments(messages) {
|
|
677
|
+
+ return messages.some(message => Array.isArray(message.content) &&
|
|
678
|
+
+ message.content.some(block => block.type === "document" || block.documentSource !== undefined));
|
|
679
|
+
+}
|
|
680
|
+
+export function documentRequestLimit(model) {
|
|
681
|
+
+ // OpenAI documents a 50 MB aggregate file limit. This local wire ceiling is stricter after base64.
|
|
682
|
+
+ // Claude first-party documents 128 MB. Subscription transport keeps a conservative 24 MiB ceiling.
|
|
683
|
+
+ if (model.provider === "openai" && model.api === "openai-responses") return 50_000_000;
|
|
684
|
+
+ if (model.provider === "anthropic" && model.api === "anthropic-messages") return 128_000_000;
|
|
685
|
+
+ return 24 * 1024 * 1024;
|
|
686
|
+
+}
|
|
687
|
+
+export function assertDocumentRequestBudget(payload, model, messages) {
|
|
688
|
+
+ if (!hasDocuments(messages)) return;
|
|
689
|
+
+ const bytes = Buffer.byteLength(JSON.stringify(payload));
|
|
690
|
+
+ if (bytes > documentRequestLimit(model))
|
|
691
|
+
+ throw new DocumentInputError("attachment_request_limit");
|
|
692
|
+
+}
|
|
693
|
+
+const representationBlock = z.discriminatedUnion("type", [
|
|
694
|
+
+ z.object({ type: z.literal("text"), text: z.string().max(49152) }).strict(),
|
|
695
|
+
+ z.object({ type: z.literal("image"), mimeType: z.enum(["image/png", "image/jpeg"]),
|
|
696
|
+
+ data: z.string().max(5592408) }).strict(),
|
|
697
|
+
+ documentContentSchema,
|
|
698
|
+
+]);
|
|
699
|
+
+const representationSchema = z.object({
|
|
700
|
+
+ status: z.literal("ready"), originalSha256: documentSha, representationSha256: documentSha,
|
|
701
|
+
+ payloadSha256: documentSha,
|
|
702
|
+
+ grantRevision: z.number().int().positive(), estimatedTokens: z.number().int().nonnegative(),
|
|
703
|
+
+ content: z.array(representationBlock).min(1).max(2000),
|
|
704
|
+
+ coverage: z.object({
|
|
705
|
+
+ totalPages: z.number().int().min(1).max(300),
|
|
706
|
+
+ pages: z.array(z.object({ page: z.number().int().min(1).max(300),
|
|
707
|
+
+ representation: z.enum(["native-text", "rendered-image", "ocr-text", "verified-blank"]) }).strict()).max(300),
|
|
708
|
+
+ }).strict(),
|
|
709
|
+
+ nextCursor: z.string().min(1).optional(),
|
|
710
|
+
+}).strict();
|
|
711
|
+
+/** Resolve on every request, including replay and provider changes. There is no host-path reader or cache. */
|
|
712
|
+
+export async function resolveDocumentMessages(messages, model, boundary) {
|
|
713
|
+
+ const result = [];
|
|
714
|
+
+ let wireBytes = Buffer.byteLength(JSON.stringify(messages));
|
|
715
|
+
+ let inputTokens = Math.ceil(wireBytes / 3);
|
|
716
|
+
+ let nativePages = 0;
|
|
717
|
+
+ for (const message of messages) {
|
|
718
|
+
+ if (!Array.isArray(message.content) || !message.content.some(block => block.type === "document")) {
|
|
719
|
+
+ result.push(message);
|
|
720
|
+
+ continue;
|
|
721
|
+
+ }
|
|
722
|
+
+ if (message.role !== "user" || message.origin !== "owner-attachment" || !message.clientInputId)
|
|
723
|
+
+ throw new DocumentInputError("attachment_document_origin");
|
|
724
|
+
+ const content = [];
|
|
725
|
+
+ for (const block of message.content) {
|
|
726
|
+
+ if (block.type !== "document") { content.push(block); continue; }
|
|
727
|
+
+ const reference = documentReferenceSchema.parse(block);
|
|
728
|
+
+ const metadata = message.attachments?.find(item => item.id === reference.source.attachmentId);
|
|
729
|
+
+ if (!metadata || metadata.grantId !== reference.source.grantId ||
|
|
730
|
+
+ metadata.sha256 !== reference.source.originalSha256)
|
|
731
|
+
+ throw new DocumentInputError("attachment_document_identity");
|
|
732
|
+
+ let mode = documentReadMode(model);
|
|
733
|
+
+ const nativeLimit = model.provider === "anthropic" && model.contextWindow < 1_000_000 ? 100 : 300;
|
|
734
|
+
+ if (mode === "native-document" && (nativePages + (metadata.pageCount ?? 300) > nativeLimit ||
|
|
735
|
+
+ wireBytes + Math.ceil(metadata.bytes / 3) * 4 + 65536 > documentRequestLimit(model)))
|
|
736
|
+
+ mode = "pages";
|
|
737
|
+
+ const readRepresentation = async () => {
|
|
738
|
+
+ const response = boundary.resolve === undefined ? { status: "attachment_unavailable" } :
|
|
739
|
+
+ await boundary.resolve({ reference, mode, sessionId: boundary.sessionId,
|
|
740
|
+
+ inputId: boundary.inputId, originInputId: message.clientInputId,
|
|
741
|
+
+ model, signal: boundary.signal });
|
|
742
|
+
+ if (["attachment_revoked", "attachment_unavailable", "attachment_text_held"].includes(response.status))
|
|
743
|
+
+ return response;
|
|
744
|
+
+ const parsed = representationSchema.safeParse(response);
|
|
745
|
+
+ if (!parsed.success) throw new DocumentInputError("attachment_representation_invalid");
|
|
746
|
+
+ const representation = parsed.data;
|
|
747
|
+
+ if (representation.originalSha256 !== reference.source.originalSha256)
|
|
748
|
+
+ throw new DocumentInputError("attachment_document_integrity");
|
|
749
|
+
+ const representationDigest = createHash("sha256")
|
|
750
|
+
+ .update(JSON.stringify({ content: representation.content, coverage: representation.coverage,
|
|
751
|
+
+ ...(representation.nextCursor === undefined ? {} : { nextCursor: representation.nextCursor }) })).digest("hex");
|
|
752
|
+
+ if (representationDigest !== representation.payloadSha256)
|
|
753
|
+
+ throw new DocumentInputError("attachment_representation_integrity");
|
|
754
|
+
+ return representation;
|
|
755
|
+
+ };
|
|
756
|
+
+ let representation = await readRepresentation();
|
|
757
|
+
+ if (representation.status === "ready" && mode === "native-document" &&
|
|
758
|
+
+ (wireBytes + Buffer.byteLength(JSON.stringify(representation.content)) + 65536 > documentRequestLimit(model) ||
|
|
759
|
+
+ (model.contextWindow > 0 && inputTokens + representation.estimatedTokens + model.maxTokens > model.contextWindow))) {
|
|
760
|
+
+ // This is another local representation selection, before any provider dispatch, not a model retry.
|
|
761
|
+
+ mode = "pages";
|
|
762
|
+
+ representation = await readRepresentation();
|
|
763
|
+
+ }
|
|
764
|
+
+ if (representation.status === "attachment_revoked" || representation.status === "attachment_unavailable") {
|
|
765
|
+
+ content.push({ type: "text", text: JSON.stringify({ type: representation.status,
|
|
766
|
+
+ attachmentId: reference.source.attachmentId, grantId: reference.source.grantId }),
|
|
767
|
+
+ documentSource: reference.source });
|
|
768
|
+
+ continue;
|
|
769
|
+
+ }
|
|
770
|
+
+ if (representation.status === "attachment_text_held") throw new DocumentInputError(representation.status);
|
|
771
|
+
+ const pages = representation.coverage.pages;
|
|
772
|
+
+ if (pages.length === 0 || new Set(pages.map(page => page.page)).size !== pages.length ||
|
|
773
|
+
+ (metadata.pageCount !== undefined && metadata.pageCount !== representation.coverage.totalPages) ||
|
|
774
|
+
+ pages.some(page => page.page > representation.coverage.totalPages) ||
|
|
775
|
+
+ (pages.length < representation.coverage.totalPages && !representation.nextCursor))
|
|
776
|
+
+ throw new DocumentInputError("attachment_coverage_incomplete");
|
|
777
|
+
+ // Ready means the complete representation was prepared; coverage is only this bounded delivery.
|
|
778
|
+
+ if (mode === "ocr" && pages.some(page => !["ocr-text", "verified-blank"].includes(page.representation)))
|
|
779
|
+
+ throw new DocumentInputError("attachment_ocr_incomplete");
|
|
780
|
+
+ let images = 0;
|
|
781
|
+
+ let documents = 0;
|
|
782
|
+
+ let textBytes = 0;
|
|
783
|
+
+ const blocks = representation.content.map(part => {
|
|
784
|
+
+ if (part.type === "text") textBytes += Buffer.byteLength(part.text);
|
|
785
|
+
+ if (part.type === "document") {
|
|
786
|
+
+ documents++;
|
|
787
|
+
+ const document = checkedDocument(part);
|
|
788
|
+
+ if (mode !== "native-document" || document.provenance.grantId !== reference.source.grantId ||
|
|
789
|
+
+ document.provenance.attachmentId !== reference.source.attachmentId ||
|
|
790
|
+
+ document.provenance.originalSha256 !== reference.source.originalSha256)
|
|
791
|
+
+ throw new DocumentInputError("attachment_document_route");
|
|
792
|
+
+ }
|
|
793
|
+
+ if (part.type === "image") {
|
|
794
|
+
+ images++;
|
|
795
|
+
+ const bytes = Buffer.from(part.data, "base64");
|
|
796
|
+
+ if (mode === "ocr" || bytes.length > 4194304 || bytes.toString("base64") !== part.data ||
|
|
797
|
+
+ (!bytes.subarray(0, 8).equals(Buffer.from([137,80,78,71,13,10,26,10])) &&
|
|
798
|
+
+ !(bytes[0] === 255 && bytes[1] === 216)))
|
|
799
|
+
+ throw new DocumentInputError("attachment_image_invalid");
|
|
800
|
+
+ }
|
|
801
|
+
+ return { ...part, documentSource: reference.source };
|
|
802
|
+
+ });
|
|
803
|
+
+ if (images > 2 || (mode === "native-document" ? documents !== 1 : documents !== 0))
|
|
804
|
+
+ throw new DocumentInputError("attachment_document_route");
|
|
805
|
+
+ if (textBytes > 49152) throw new DocumentInputError("attachment_text_limit");
|
|
806
|
+
+ if (mode !== "native-document" &&
|
|
807
|
+
+ (images !== pages.filter(page => page.representation === "rendered-image").length ||
|
|
808
|
+
+ (!representation.content.some(part => part.type === "text" && part.text.trim() !== "") && pages.some(page => ["native-text", "ocr-text"].includes(page.representation)))))
|
|
809
|
+
+ throw new DocumentInputError("attachment_coverage_incomplete");
|
|
810
|
+
+ nativePages += mode === "native-document" ? representation.coverage.totalPages : 0;
|
|
811
|
+
+ const coverage = { type: "attachment_coverage", source: reference.source,
|
|
812
|
+
+ representationSha256: representation.representationSha256, grantRevision: representation.grantRevision,
|
|
813
|
+
+ coverage: representation.coverage, nextCursor: representation.nextCursor };
|
|
814
|
+
+ content.push({ type: "text", text: JSON.stringify(coverage), documentSource: reference.source }, ...blocks);
|
|
815
|
+
+ wireBytes += Buffer.byteLength(JSON.stringify(blocks));
|
|
816
|
+
+ inputTokens += representation.estimatedTokens;
|
|
817
|
+
+ if (wireBytes + 65536 > documentRequestLimit(model) ||
|
|
818
|
+
+ (model.contextWindow > 0 && inputTokens + model.maxTokens > model.contextWindow))
|
|
819
|
+
+ throw new DocumentInputError("attachment_request_limit");
|
|
820
|
+
+ }
|
|
821
|
+
+ result.push({ ...message, content });
|
|
822
|
+
+ }
|
|
823
|
+
+ return result;
|
|
824
|
+
+}
|
|
825
|
+
--- a/@earendil-works/pi-ai/dist/utils/estimate.js
|
|
826
|
+
+++ b/@earendil-works/pi-ai/dist/utils/estimate.js
|
|
827
|
+
@@ -24,1 +24,3 @@
|
|
828
|
+
- chars += block.type === "text" ? block.text.length : ESTIMATED_IMAGE_CHARS;
|
|
829
|
+
+ chars += block.type === "text" ? block.text.length : block.type === "document"
|
|
830
|
+
+ ? (block.data ? block.data.length * 4 : JSON.stringify(block).length + ESTIMATED_IMAGE_CHARS)
|
|
831
|
+
+ : ESTIMATED_IMAGE_CHARS;
|