bernard-agent 0.8.0 → 0.9.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.
- package/README.md +80 -44
- package/dist/agent.d.ts +14 -3
- package/dist/agent.js +228 -38
- package/dist/agent.js.map +1 -1
- package/dist/builtin-specialists/correction-agent.json +32 -0
- package/dist/builtin-specialists/file-wrapper.json +43 -0
- package/dist/builtin-specialists/shell-wrapper.json +50 -0
- package/dist/builtin-specialists/specialist-creator.json +32 -0
- package/dist/builtin-specialists/web-wrapper.json +38 -0
- package/dist/candidate-bootstrap.d.ts +18 -0
- package/dist/candidate-bootstrap.js +61 -0
- package/dist/candidate-bootstrap.js.map +1 -0
- package/dist/config.d.ts +132 -10
- package/dist/config.js +234 -47
- package/dist/config.js.map +1 -1
- package/dist/context.js +23 -6
- package/dist/context.js.map +1 -1
- package/dist/correction-candidates.d.ts +54 -0
- package/dist/correction-candidates.js +138 -0
- package/dist/correction-candidates.js.map +1 -0
- package/dist/correction.d.ts +67 -0
- package/dist/correction.js +138 -0
- package/dist/correction.js.map +1 -0
- package/dist/critic.js +2 -1
- package/dist/critic.js.map +1 -1
- package/dist/cron/notes-store.d.ts +41 -0
- package/dist/cron/notes-store.js +134 -0
- package/dist/cron/notes-store.js.map +1 -0
- package/dist/cron/runner.js +25 -3
- package/dist/cron/runner.js.map +1 -1
- package/dist/cron/scoped-notes-tools.d.ts +24 -0
- package/dist/cron/scoped-notes-tools.js +50 -0
- package/dist/cron/scoped-notes-tools.js.map +1 -0
- package/dist/custom-providers.d.ts +80 -0
- package/dist/custom-providers.js +238 -0
- package/dist/custom-providers.js.map +1 -0
- package/dist/fs-utils.d.ts +2 -0
- package/dist/fs-utils.js +44 -0
- package/dist/fs-utils.js.map +1 -0
- package/dist/history.js +3 -1
- package/dist/history.js.map +1 -1
- package/dist/image.d.ts +59 -0
- package/dist/image.js +228 -0
- package/dist/image.js.map +1 -0
- package/dist/index.js +72 -4
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +1 -1
- package/dist/mcp.js.map +1 -1
- package/dist/memory.d.ts +13 -0
- package/dist/memory.js +45 -4
- package/dist/memory.js.map +1 -1
- package/dist/menu.d.ts +97 -0
- package/dist/menu.js +338 -0
- package/dist/menu.js.map +1 -0
- package/dist/os-info.d.ts +22 -0
- package/dist/os-info.js +111 -0
- package/dist/os-info.js.map +1 -0
- package/dist/output.d.ts +35 -1
- package/dist/output.js +256 -45
- package/dist/output.js.map +1 -1
- package/dist/pac.d.ts +14 -2
- package/dist/pac.js +5 -5
- package/dist/pac.js.map +1 -1
- package/dist/paths.d.ts +5 -0
- package/dist/paths.js +6 -1
- package/dist/paths.js.map +1 -1
- package/dist/plan-store.d.ts +47 -0
- package/dist/plan-store.js +94 -0
- package/dist/plan-store.js.map +1 -0
- package/dist/prompt-rewriter.d.ts +29 -0
- package/dist/prompt-rewriter.js +155 -0
- package/dist/prompt-rewriter.js.map +1 -0
- package/dist/providers/index.d.ts +56 -4
- package/dist/providers/index.js +86 -5
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/profiles.d.ts +37 -0
- package/dist/providers/profiles.js +110 -0
- package/dist/providers/profiles.js.map +1 -0
- package/dist/providers/types.d.ts +11 -2
- package/dist/providers/types.js +3 -0
- package/dist/providers/types.js.map +1 -1
- package/dist/rag-query.js +15 -1
- package/dist/rag-query.js.map +1 -1
- package/dist/react.d.ts +38 -0
- package/dist/react.js +116 -0
- package/dist/react.js.map +1 -0
- package/dist/reasoning-log.d.ts +30 -0
- package/dist/reasoning-log.js +102 -0
- package/dist/reasoning-log.js.map +1 -0
- package/dist/reference-resolver.d.ts +47 -0
- package/dist/reference-resolver.js +316 -0
- package/dist/reference-resolver.js.map +1 -0
- package/dist/reference-tool-lookup.d.ts +37 -0
- package/dist/reference-tool-lookup.js +318 -0
- package/dist/reference-tool-lookup.js.map +1 -0
- package/dist/repl.js +1053 -346
- package/dist/repl.js.map +1 -1
- package/dist/setup.js +2 -1
- package/dist/setup.js.map +1 -1
- package/dist/specialist-detector.js +2 -1
- package/dist/specialist-detector.js.map +1 -1
- package/dist/specialists.d.ts +74 -3
- package/dist/specialists.js +152 -20
- package/dist/specialists.js.map +1 -1
- package/dist/structured-output.d.ts +58 -0
- package/dist/structured-output.js +138 -0
- package/dist/structured-output.js.map +1 -0
- package/dist/theme.d.ts +2 -0
- package/dist/theme.js +18 -12
- package/dist/theme.js.map +1 -1
- package/dist/tool-call-repair.d.ts +29 -0
- package/dist/tool-call-repair.js +99 -0
- package/dist/tool-call-repair.js.map +1 -0
- package/dist/tool-profiles.d.ts +70 -0
- package/dist/tool-profiles.js +385 -0
- package/dist/tool-profiles.js.map +1 -0
- package/dist/tools/activity-summary.d.ts +15 -0
- package/dist/tools/activity-summary.js +44 -0
- package/dist/tools/activity-summary.js.map +1 -0
- package/dist/tools/ask-user.d.ts +49 -0
- package/dist/tools/ask-user.js +52 -0
- package/dist/tools/ask-user.js.map +1 -0
- package/dist/tools/augment.d.ts +17 -0
- package/dist/tools/augment.js +102 -0
- package/dist/tools/augment.js.map +1 -0
- package/dist/tools/cron-logs.js +7 -0
- package/dist/tools/cron-logs.js.map +1 -1
- package/dist/tools/cron-notes.d.ts +52 -0
- package/dist/tools/cron-notes.js +105 -0
- package/dist/tools/cron-notes.js.map +1 -0
- package/dist/tools/datetime.d.ts +7 -0
- package/dist/tools/datetime.js +29 -3
- package/dist/tools/datetime.js.map +1 -1
- package/dist/tools/evaluate.d.ts +20 -0
- package/dist/tools/evaluate.js +29 -0
- package/dist/tools/evaluate.js.map +1 -0
- package/dist/tools/index.js +4 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/mcp.d.ts +3 -3
- package/dist/tools/plan.d.ts +81 -0
- package/dist/tools/plan.js +108 -0
- package/dist/tools/plan.js.map +1 -0
- package/dist/tools/result-cap.d.ts +24 -0
- package/dist/tools/result-cap.js +44 -0
- package/dist/tools/result-cap.js.map +1 -0
- package/dist/tools/routine.d.ts +3 -3
- package/dist/tools/shell.d.ts +14 -1
- package/dist/tools/shell.js +86 -4
- package/dist/tools/shell.js.map +1 -1
- package/dist/tools/specialist-run.d.ts +5 -3
- package/dist/tools/specialist-run.js +115 -24
- package/dist/tools/specialist-run.js.map +1 -1
- package/dist/tools/specialist.d.ts +83 -3
- package/dist/tools/specialist.js +83 -3
- package/dist/tools/specialist.js.map +1 -1
- package/dist/tools/subagent.js +32 -14
- package/dist/tools/subagent.js.map +1 -1
- package/dist/tools/task.d.ts +19 -16
- package/dist/tools/task.js +69 -40
- package/dist/tools/task.js.map +1 -1
- package/dist/tools/think.d.ts +18 -0
- package/dist/tools/think.js +25 -0
- package/dist/tools/think.js.map +1 -0
- package/dist/tools/tool-wrapper-run.d.ts +121 -0
- package/dist/tools/tool-wrapper-run.js +382 -0
- package/dist/tools/tool-wrapper-run.js.map +1 -0
- package/dist/tools/types.d.ts +28 -2
- package/dist/tools/web-search.d.ts +31 -0
- package/dist/tools/web-search.js +172 -0
- package/dist/tools/web-search.js.map +1 -0
- package/dist/tools/wrap-with-specialist.d.ts +55 -0
- package/dist/tools/wrap-with-specialist.js +137 -0
- package/dist/tools/wrap-with-specialist.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @module custom-providers
|
|
4
|
+
*
|
|
5
|
+
* Disk-backed registry of user-defined custom providers — named endpoints
|
|
6
|
+
* that wrap one of the installed AI SDKs (`@ai-sdk/openai`,
|
|
7
|
+
* `@ai-sdk/anthropic`, `@ai-sdk/xai`) with a custom `baseURL` and API key.
|
|
8
|
+
*
|
|
9
|
+
* Lets the user route Bernard at OpenAI/Anthropic/xAI-compatible servers
|
|
10
|
+
* (Ollama, LM Studio, vLLM, OpenRouter, Together, internal proxies, ...)
|
|
11
|
+
* and have several such endpoints configured at once alongside the
|
|
12
|
+
* built-in providers.
|
|
13
|
+
*
|
|
14
|
+
* Storage: `~/.config/bernard/custom-providers.json`
|
|
15
|
+
* Shape: `{ providers: Record<name, CustomProvider> }` with file mode 0600.
|
|
16
|
+
*
|
|
17
|
+
* API keys are NOT stored here; they live in `keys.json` keyed by the
|
|
18
|
+
* provider name (the same file the built-in providers use).
|
|
19
|
+
*/
|
|
20
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
21
|
+
if (k2 === undefined) k2 = k;
|
|
22
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
23
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
24
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
25
|
+
}
|
|
26
|
+
Object.defineProperty(o, k2, desc);
|
|
27
|
+
}) : (function(o, m, k, k2) {
|
|
28
|
+
if (k2 === undefined) k2 = k;
|
|
29
|
+
o[k2] = m[k];
|
|
30
|
+
}));
|
|
31
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
32
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
33
|
+
}) : function(o, v) {
|
|
34
|
+
o["default"] = v;
|
|
35
|
+
});
|
|
36
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
37
|
+
var ownKeys = function(o) {
|
|
38
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
39
|
+
var ar = [];
|
|
40
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
41
|
+
return ar;
|
|
42
|
+
};
|
|
43
|
+
return ownKeys(o);
|
|
44
|
+
};
|
|
45
|
+
return function (mod) {
|
|
46
|
+
if (mod && mod.__esModule) return mod;
|
|
47
|
+
var result = {};
|
|
48
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
49
|
+
__setModuleDefault(result, mod);
|
|
50
|
+
return result;
|
|
51
|
+
};
|
|
52
|
+
})();
|
|
53
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
54
|
+
exports.SUPPORTED_SDKS = exports.RESERVED_PROVIDER_NAMES = void 0;
|
|
55
|
+
exports.validateProviderName = validateProviderName;
|
|
56
|
+
exports.validateBaseURL = validateBaseURL;
|
|
57
|
+
exports.loadCustomProviders = loadCustomProviders;
|
|
58
|
+
exports.saveCustomProvider = saveCustomProvider;
|
|
59
|
+
exports.removeCustomProvider = removeCustomProvider;
|
|
60
|
+
exports.rememberCustomModel = rememberCustomModel;
|
|
61
|
+
exports.isCustomProvider = isCustomProvider;
|
|
62
|
+
const fs = __importStar(require("node:fs"));
|
|
63
|
+
const path = __importStar(require("node:path"));
|
|
64
|
+
const paths_js_1 = require("./paths.js");
|
|
65
|
+
const fs_utils_js_1 = require("./fs-utils.js");
|
|
66
|
+
/** Names reserved by built-in providers — cannot be used for custom entries. */
|
|
67
|
+
exports.RESERVED_PROVIDER_NAMES = new Set(['anthropic', 'openai', 'xai']);
|
|
68
|
+
/** Allowed SDK choices for a custom provider. */
|
|
69
|
+
exports.SUPPORTED_SDKS = ['openai', 'anthropic', 'xai'];
|
|
70
|
+
/** Max remembered models per custom provider (oldest entries drop when full). */
|
|
71
|
+
const MODELS_CAP = 20;
|
|
72
|
+
const NAME_PATTERN = /^[a-z][a-z0-9_-]*$/;
|
|
73
|
+
const NAME_MAX_LENGTH = 32;
|
|
74
|
+
/**
|
|
75
|
+
* Validates a custom provider name.
|
|
76
|
+
* @returns An error message if invalid, or `null` if valid.
|
|
77
|
+
*/
|
|
78
|
+
function validateProviderName(name) {
|
|
79
|
+
if (!name)
|
|
80
|
+
return 'Provider name cannot be empty.';
|
|
81
|
+
if (name.length > NAME_MAX_LENGTH)
|
|
82
|
+
return `Provider name must be ${NAME_MAX_LENGTH} characters or fewer.`;
|
|
83
|
+
if (!NAME_PATTERN.test(name))
|
|
84
|
+
return 'Provider name must start with a lowercase letter and contain only lowercase letters, digits, hyphens, and underscores.';
|
|
85
|
+
if (exports.RESERVED_PROVIDER_NAMES.has(name))
|
|
86
|
+
return `"${name}" is a built-in provider name; pick a different name for the custom entry.`;
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Validates a base URL — must parse, and use http/https.
|
|
91
|
+
* @returns An error message if invalid, or `null` if valid.
|
|
92
|
+
*/
|
|
93
|
+
function validateBaseURL(url) {
|
|
94
|
+
if (!url || !url.trim())
|
|
95
|
+
return 'Base URL cannot be empty.';
|
|
96
|
+
let parsed;
|
|
97
|
+
try {
|
|
98
|
+
parsed = new URL(url);
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return 'Base URL is not a valid URL.';
|
|
102
|
+
}
|
|
103
|
+
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:')
|
|
104
|
+
return 'Base URL must use http or https.';
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
/** Reads the custom-providers file. Fails open to an empty map on any error. */
|
|
108
|
+
function loadCustomProviders() {
|
|
109
|
+
try {
|
|
110
|
+
const raw = fs.readFileSync(paths_js_1.CUSTOM_PROVIDERS_PATH, 'utf-8');
|
|
111
|
+
const parsed = JSON.parse(raw);
|
|
112
|
+
if (parsed &&
|
|
113
|
+
typeof parsed === 'object' &&
|
|
114
|
+
parsed.providers &&
|
|
115
|
+
typeof parsed.providers === 'object') {
|
|
116
|
+
const out = {};
|
|
117
|
+
for (const [name, entry] of Object.entries(parsed.providers)) {
|
|
118
|
+
if (!entry || typeof entry !== 'object')
|
|
119
|
+
continue;
|
|
120
|
+
const e = entry;
|
|
121
|
+
if (typeof e.name === 'string' &&
|
|
122
|
+
typeof e.sdk === 'string' &&
|
|
123
|
+
exports.SUPPORTED_SDKS.includes(e.sdk) &&
|
|
124
|
+
typeof e.baseURL === 'string' &&
|
|
125
|
+
typeof e.defaultModel === 'string') {
|
|
126
|
+
const models = Array.isArray(e.models)
|
|
127
|
+
? e.models.filter((m) => typeof m === 'string' && m.length > 0)
|
|
128
|
+
: [e.defaultModel];
|
|
129
|
+
if (!models.includes(e.defaultModel))
|
|
130
|
+
models.unshift(e.defaultModel);
|
|
131
|
+
out[name] = {
|
|
132
|
+
name: e.name,
|
|
133
|
+
sdk: e.sdk,
|
|
134
|
+
baseURL: e.baseURL,
|
|
135
|
+
defaultModel: e.defaultModel,
|
|
136
|
+
models,
|
|
137
|
+
createdAt: typeof e.createdAt === 'string' ? e.createdAt : new Date().toISOString(),
|
|
138
|
+
updatedAt: typeof e.updatedAt === 'string' ? e.updatedAt : new Date().toISOString(),
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return out;
|
|
143
|
+
}
|
|
144
|
+
return {};
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
return {};
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
function writeFile(providers) {
|
|
151
|
+
fs.mkdirSync(path.dirname(paths_js_1.CUSTOM_PROVIDERS_PATH), { recursive: true });
|
|
152
|
+
const payload = { providers };
|
|
153
|
+
(0, fs_utils_js_1.atomicWriteFileSync)(paths_js_1.CUSTOM_PROVIDERS_PATH, JSON.stringify(payload, null, 2) + '\n');
|
|
154
|
+
fs.chmodSync(paths_js_1.CUSTOM_PROVIDERS_PATH, 0o600);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Inserts or updates a custom provider. Validates name / sdk / baseURL /
|
|
158
|
+
* defaultModel and ensures `defaultModel` is the first entry in `models`.
|
|
159
|
+
*
|
|
160
|
+
* @throws {Error} If any field fails validation.
|
|
161
|
+
*/
|
|
162
|
+
function saveCustomProvider(input) {
|
|
163
|
+
const nameErr = validateProviderName(input.name);
|
|
164
|
+
if (nameErr)
|
|
165
|
+
throw new Error(nameErr);
|
|
166
|
+
if (!exports.SUPPORTED_SDKS.includes(input.sdk)) {
|
|
167
|
+
throw new Error(`Unsupported SDK "${input.sdk}". Supported: ${exports.SUPPORTED_SDKS.join(', ')}.`);
|
|
168
|
+
}
|
|
169
|
+
const urlErr = validateBaseURL(input.baseURL);
|
|
170
|
+
if (urlErr)
|
|
171
|
+
throw new Error(urlErr);
|
|
172
|
+
const defaultModel = input.defaultModel.trim();
|
|
173
|
+
if (!defaultModel)
|
|
174
|
+
throw new Error('Default model cannot be empty.');
|
|
175
|
+
const existing = loadCustomProviders();
|
|
176
|
+
const now = new Date().toISOString();
|
|
177
|
+
const seedModels = input.models?.filter((m) => m && m.trim().length > 0) ?? [];
|
|
178
|
+
const models = [defaultModel, ...seedModels.filter((m) => m !== defaultModel)];
|
|
179
|
+
const entry = {
|
|
180
|
+
name: input.name,
|
|
181
|
+
sdk: input.sdk,
|
|
182
|
+
baseURL: input.baseURL,
|
|
183
|
+
defaultModel,
|
|
184
|
+
models: models.slice(0, MODELS_CAP),
|
|
185
|
+
createdAt: existing[input.name]?.createdAt ?? now,
|
|
186
|
+
updatedAt: now,
|
|
187
|
+
};
|
|
188
|
+
existing[input.name] = entry;
|
|
189
|
+
writeFile(existing);
|
|
190
|
+
return entry;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Deletes a custom provider entry. Does NOT delete the matching key in
|
|
194
|
+
* `keys.json` — callers (CLI / REPL) handle key cleanup explicitly.
|
|
195
|
+
*
|
|
196
|
+
* @throws {Error} If no custom provider with that name exists.
|
|
197
|
+
*/
|
|
198
|
+
function removeCustomProvider(name) {
|
|
199
|
+
const existing = loadCustomProviders();
|
|
200
|
+
if (!existing[name]) {
|
|
201
|
+
throw new Error(`No custom provider named "${name}".`);
|
|
202
|
+
}
|
|
203
|
+
delete existing[name];
|
|
204
|
+
if (Object.keys(existing).length === 0) {
|
|
205
|
+
if (fs.existsSync(paths_js_1.CUSTOM_PROVIDERS_PATH)) {
|
|
206
|
+
fs.unlinkSync(paths_js_1.CUSTOM_PROVIDERS_PATH);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
writeFile(existing);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Adds `model` to the remembered list for a custom provider if not already
|
|
215
|
+
* present. The default model always stays at position 0. No-op when the
|
|
216
|
+
* provider is unknown.
|
|
217
|
+
*/
|
|
218
|
+
function rememberCustomModel(name, model) {
|
|
219
|
+
const trimmed = model.trim();
|
|
220
|
+
if (!trimmed)
|
|
221
|
+
return;
|
|
222
|
+
const existing = loadCustomProviders();
|
|
223
|
+
const entry = existing[name];
|
|
224
|
+
if (!entry)
|
|
225
|
+
return;
|
|
226
|
+
if (entry.models.includes(trimmed))
|
|
227
|
+
return;
|
|
228
|
+
const next = [...entry.models, trimmed].slice(-MODELS_CAP);
|
|
229
|
+
// Default model always first.
|
|
230
|
+
const reordered = [entry.defaultModel, ...next.filter((m) => m !== entry.defaultModel)];
|
|
231
|
+
existing[name] = { ...entry, models: reordered, updatedAt: new Date().toISOString() };
|
|
232
|
+
writeFile(existing);
|
|
233
|
+
}
|
|
234
|
+
/** Returns true if `name` matches an existing custom provider entry. */
|
|
235
|
+
function isCustomProvider(name, customProviders = loadCustomProviders()) {
|
|
236
|
+
return Object.hasOwn(customProviders, name);
|
|
237
|
+
}
|
|
238
|
+
//# sourceMappingURL=custom-providers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom-providers.js","sourceRoot":"","sources":["../src/custom-providers.ts"],"names":[],"mappings":"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"}
|
package/dist/fs-utils.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.atomicWriteFileSync = atomicWriteFileSync;
|
|
37
|
+
const fs = __importStar(require("node:fs"));
|
|
38
|
+
/** Writes `data` to a `.tmp` file then renames it into place for crash-safe persistence. */
|
|
39
|
+
function atomicWriteFileSync(filePath, data) {
|
|
40
|
+
const tmp = filePath + '.tmp';
|
|
41
|
+
fs.writeFileSync(tmp, data, 'utf-8');
|
|
42
|
+
fs.renameSync(tmp, filePath);
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=fs-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fs-utils.js","sourceRoot":"","sources":["../src/fs-utils.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGA,kDAIC;AAPD,4CAA8B;AAE9B,4FAA4F;AAC5F,SAAgB,mBAAmB,CAAC,QAAgB,EAAE,IAAY;IAChE,MAAM,GAAG,GAAG,QAAQ,GAAG,MAAM,CAAC;IAC9B,EAAE,CAAC,aAAa,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IACrC,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;AAC/B,CAAC"}
|
package/dist/history.js
CHANGED
|
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.HistoryStore = void 0;
|
|
37
37
|
const fs = __importStar(require("node:fs"));
|
|
38
38
|
const paths_js_1 = require("./paths.js");
|
|
39
|
+
const image_js_1 = require("./image.js");
|
|
39
40
|
/**
|
|
40
41
|
* Manages persistence of conversation history.
|
|
41
42
|
*
|
|
@@ -58,8 +59,9 @@ class HistoryStore {
|
|
|
58
59
|
/** Atomically writes the conversation history to disk. */
|
|
59
60
|
save(messages) {
|
|
60
61
|
fs.mkdirSync(paths_js_1.STATE_DIR, { recursive: true });
|
|
62
|
+
const stripped = (0, image_js_1.stripImagesFromHistory)(messages);
|
|
61
63
|
const tmp = paths_js_1.HISTORY_FILE + '.tmp';
|
|
62
|
-
fs.writeFileSync(tmp, JSON.stringify(
|
|
64
|
+
fs.writeFileSync(tmp, JSON.stringify(stripped, null, 2), 'utf-8');
|
|
63
65
|
fs.renameSync(tmp, paths_js_1.HISTORY_FILE);
|
|
64
66
|
}
|
|
65
67
|
/** Deletes the saved history file. Silently succeeds if the file does not exist. */
|
package/dist/history.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"history.js","sourceRoot":"","sources":["../src/history.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,4CAA8B;AAE9B,yCAAqD;
|
|
1
|
+
{"version":3,"file":"history.js","sourceRoot":"","sources":["../src/history.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,4CAA8B;AAE9B,yCAAqD;AACrD,yCAAoD;AAEpD;;;;GAIG;AACH,MAAa,YAAY;IACvB,oHAAoH;IACpH,IAAI;QACF,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,EAAE,CAAC,YAAY,CAAC,uBAAY,EAAE,OAAO,CAAC,CAAC;YACpD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAChC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;gBAAE,OAAO,EAAE,CAAC;YACtC,OAAO,MAAM,CAAC,MAAM,CAClB,CAAC,KAAc,EAAE,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,MAAM,IAAI,KAAK,CAClE,CAAC;QACrB,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IAED,0DAA0D;IAC1D,IAAI,CAAC,QAAuB;QAC1B,EAAE,CAAC,SAAS,CAAC,oBAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7C,MAAM,QAAQ,GAAG,IAAA,iCAAsB,EAAC,QAAQ,CAAC,CAAC;QAClD,MAAM,GAAG,GAAG,uBAAY,GAAG,MAAM,CAAC;QAClC,EAAE,CAAC,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;QAClE,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,uBAAY,CAAC,CAAC;IACnC,CAAC;IAED,oFAAoF;IACpF,KAAK;QACH,IAAI,CAAC;YACH,EAAE,CAAC,UAAU,CAAC,uBAAY,CAAC,CAAC;QAC9B,CAAC;QAAC,MAAM,CAAC;YACP,8BAA8B;QAChC,CAAC;IACH,CAAC;CACF;AAhCD,oCAgCC"}
|
package/dist/image.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { CoreMessage } from 'ai';
|
|
2
|
+
/** Describes a loaded image ready to be attached to a user message. */
|
|
3
|
+
export interface ImageAttachment {
|
|
4
|
+
/** Resolved absolute path (for display/logging). */
|
|
5
|
+
path: string;
|
|
6
|
+
/** MIME type, e.g. `'image/png'`. */
|
|
7
|
+
mimeType: string;
|
|
8
|
+
/** Raw image bytes — the AI SDK accepts `Buffer` as `DataContent`. */
|
|
9
|
+
data: Buffer;
|
|
10
|
+
}
|
|
11
|
+
/** Map of lowercase file extensions to MIME types supported by vision models. */
|
|
12
|
+
export declare const SUPPORTED_EXTENSIONS: Map<string, string>;
|
|
13
|
+
/**
|
|
14
|
+
* Flat token estimate per image for pre-flight compression/truncation checks.
|
|
15
|
+
* Actual token costs vary by model and resolution, but 1000 is a safe overestimate.
|
|
16
|
+
*/
|
|
17
|
+
export declare const IMAGE_TOKEN_ESTIMATE = 1000;
|
|
18
|
+
/** Returns the MIME type for a file path based on its extension, or `null` if unsupported. */
|
|
19
|
+
export declare function detectMimeType(filePath: string): string | null;
|
|
20
|
+
/**
|
|
21
|
+
* Loads and validates an image file from disk.
|
|
22
|
+
* @throws {Error} If the file does not exist, is a directory, exceeds 10 MB, or has an unsupported extension.
|
|
23
|
+
*/
|
|
24
|
+
export declare function loadImage(filePath: string): ImageAttachment;
|
|
25
|
+
/**
|
|
26
|
+
* Like `loadImage`, but returns `null` instead of throwing.
|
|
27
|
+
* Used for inline detection where a non-existent or invalid file should be silently skipped.
|
|
28
|
+
*/
|
|
29
|
+
export declare function tryLoadImage(filePath: string): ImageAttachment | null;
|
|
30
|
+
/**
|
|
31
|
+
* Scans user text for tokens that look like file paths ending in supported image extensions.
|
|
32
|
+
* Returns the extracted path strings (with `~` expansion applied).
|
|
33
|
+
*/
|
|
34
|
+
export declare function extractImagePaths(text: string): string[];
|
|
35
|
+
/**
|
|
36
|
+
* Removes image-path tokens from user text. Used to sanitize input before handing it to
|
|
37
|
+
* the reference resolver so attachment paths aren't mistaken for unresolved entities.
|
|
38
|
+
*/
|
|
39
|
+
export declare function stripImagePaths(text: string): string;
|
|
40
|
+
/**
|
|
41
|
+
* Heuristic check for whether the given provider/model combination supports image input.
|
|
42
|
+
*
|
|
43
|
+
* - Anthropic: all models are treated as vision-capable (all current models are Claude 3+).
|
|
44
|
+
* - OpenAI: gpt-4o, gpt-4.1, gpt-4-turbo, gpt-5, o3, and o4 families are treated as vision-capable.
|
|
45
|
+
* - xAI: models containing `vision` in the name, plus grok-4 family, are treated as vision-capable.
|
|
46
|
+
* - Unknown providers: optimistically allowed (the API will reject if unsupported).
|
|
47
|
+
*/
|
|
48
|
+
export declare function isVisionCapableModel(provider: string, model: string): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Estimates the token count contribution of a single content part.
|
|
51
|
+
* For image parts, returns the flat IMAGE_TOKEN_ESTIMATE instead of serializing the binary data.
|
|
52
|
+
*/
|
|
53
|
+
export declare function estimateContentPartTokens(part: unknown): number;
|
|
54
|
+
/**
|
|
55
|
+
* Returns a new history array where `ImagePart` entries in user messages are replaced
|
|
56
|
+
* with a `[Image attached]` text placeholder. Does not mutate the original.
|
|
57
|
+
* Used before persisting history to disk to avoid writing base64 data.
|
|
58
|
+
*/
|
|
59
|
+
export declare function stripImagesFromHistory(history: CoreMessage[]): CoreMessage[];
|
package/dist/image.js
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.IMAGE_TOKEN_ESTIMATE = exports.SUPPORTED_EXTENSIONS = void 0;
|
|
37
|
+
exports.detectMimeType = detectMimeType;
|
|
38
|
+
exports.loadImage = loadImage;
|
|
39
|
+
exports.tryLoadImage = tryLoadImage;
|
|
40
|
+
exports.extractImagePaths = extractImagePaths;
|
|
41
|
+
exports.stripImagePaths = stripImagePaths;
|
|
42
|
+
exports.isVisionCapableModel = isVisionCapableModel;
|
|
43
|
+
exports.estimateContentPartTokens = estimateContentPartTokens;
|
|
44
|
+
exports.stripImagesFromHistory = stripImagesFromHistory;
|
|
45
|
+
const fs = __importStar(require("node:fs"));
|
|
46
|
+
const path = __importStar(require("node:path"));
|
|
47
|
+
const os = __importStar(require("node:os"));
|
|
48
|
+
/** Map of lowercase file extensions to MIME types supported by vision models. */
|
|
49
|
+
exports.SUPPORTED_EXTENSIONS = new Map([
|
|
50
|
+
['.png', 'image/png'],
|
|
51
|
+
['.jpg', 'image/jpeg'],
|
|
52
|
+
['.jpeg', 'image/jpeg'],
|
|
53
|
+
['.gif', 'image/gif'],
|
|
54
|
+
['.webp', 'image/webp'],
|
|
55
|
+
]);
|
|
56
|
+
/** Maximum file size for image uploads (10 MB). */
|
|
57
|
+
const MAX_IMAGE_SIZE = 10 * 1024 * 1024;
|
|
58
|
+
/**
|
|
59
|
+
* Flat token estimate per image for pre-flight compression/truncation checks.
|
|
60
|
+
* Actual token costs vary by model and resolution, but 1000 is a safe overestimate.
|
|
61
|
+
*/
|
|
62
|
+
exports.IMAGE_TOKEN_ESTIMATE = 1000;
|
|
63
|
+
/**
|
|
64
|
+
* Regex matching tokens that look like file paths ending in a supported image extension.
|
|
65
|
+
* Handles absolute paths, relative paths, `~` home-dir expansion, and quoted paths.
|
|
66
|
+
*/
|
|
67
|
+
const IMAGE_PATH_RE = /(?:"([^"]+\.(?:png|jpe?g|gif|webp))"|'([^']+\.(?:png|jpe?g|gif|webp))'|((?:[~.]?\/|\.\.\/)?[\w.\-\/]+\.(?:png|jpe?g|gif|webp)))/gi;
|
|
68
|
+
/** Returns the MIME type for a file path based on its extension, or `null` if unsupported. */
|
|
69
|
+
function detectMimeType(filePath) {
|
|
70
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
71
|
+
return exports.SUPPORTED_EXTENSIONS.get(ext) ?? null;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Expands `~` at the start of a path to the user's home directory.
|
|
75
|
+
*/
|
|
76
|
+
function expandHome(filePath) {
|
|
77
|
+
if (filePath === '~') {
|
|
78
|
+
return os.homedir();
|
|
79
|
+
}
|
|
80
|
+
if (filePath.startsWith('~/')) {
|
|
81
|
+
return path.join(os.homedir(), filePath.slice(2));
|
|
82
|
+
}
|
|
83
|
+
return filePath;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Loads and validates an image file from disk.
|
|
87
|
+
* @throws {Error} If the file does not exist, is a directory, exceeds 10 MB, or has an unsupported extension.
|
|
88
|
+
*/
|
|
89
|
+
function loadImage(filePath) {
|
|
90
|
+
const resolved = path.resolve(expandHome(filePath));
|
|
91
|
+
let stat;
|
|
92
|
+
try {
|
|
93
|
+
stat = fs.statSync(resolved);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
throw new Error(`Image file not found: ${resolved}`);
|
|
97
|
+
}
|
|
98
|
+
if (stat.isDirectory()) {
|
|
99
|
+
throw new Error(`Path is a directory, not an image file: ${resolved}`);
|
|
100
|
+
}
|
|
101
|
+
if (stat.size > MAX_IMAGE_SIZE) {
|
|
102
|
+
const sizeMB = (stat.size / (1024 * 1024)).toFixed(1);
|
|
103
|
+
throw new Error(`Image file too large (${sizeMB} MB, max 10 MB): ${resolved}`);
|
|
104
|
+
}
|
|
105
|
+
const mimeType = detectMimeType(resolved);
|
|
106
|
+
if (!mimeType) {
|
|
107
|
+
const ext = path.extname(resolved);
|
|
108
|
+
throw new Error(`Unsupported image format "${ext}". Supported: ${[...exports.SUPPORTED_EXTENSIONS.keys()].join(', ')}`);
|
|
109
|
+
}
|
|
110
|
+
const data = fs.readFileSync(resolved);
|
|
111
|
+
return { path: resolved, mimeType, data };
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Like `loadImage`, but returns `null` instead of throwing.
|
|
115
|
+
* Used for inline detection where a non-existent or invalid file should be silently skipped.
|
|
116
|
+
*/
|
|
117
|
+
function tryLoadImage(filePath) {
|
|
118
|
+
try {
|
|
119
|
+
return loadImage(filePath);
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Scans user text for tokens that look like file paths ending in supported image extensions.
|
|
127
|
+
* Returns the extracted path strings (with `~` expansion applied).
|
|
128
|
+
*/
|
|
129
|
+
function extractImagePaths(text) {
|
|
130
|
+
const paths = [];
|
|
131
|
+
let match;
|
|
132
|
+
IMAGE_PATH_RE.lastIndex = 0;
|
|
133
|
+
while ((match = IMAGE_PATH_RE.exec(text)) !== null) {
|
|
134
|
+
// Groups: 1 = double-quoted, 2 = single-quoted, 3 = unquoted
|
|
135
|
+
const raw = match[1] ?? match[2] ?? match[3];
|
|
136
|
+
if (raw) {
|
|
137
|
+
paths.push(expandHome(raw));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return paths;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Removes image-path tokens from user text. Used to sanitize input before handing it to
|
|
144
|
+
* the reference resolver so attachment paths aren't mistaken for unresolved entities.
|
|
145
|
+
*/
|
|
146
|
+
function stripImagePaths(text) {
|
|
147
|
+
const re = new RegExp(IMAGE_PATH_RE.source, 'gi');
|
|
148
|
+
return text.replace(re, ' ').replace(/\s+/g, ' ').trim();
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Heuristic check for whether the given provider/model combination supports image input.
|
|
152
|
+
*
|
|
153
|
+
* - Anthropic: all models are treated as vision-capable (all current models are Claude 3+).
|
|
154
|
+
* - OpenAI: gpt-4o, gpt-4.1, gpt-4-turbo, gpt-5, o3, and o4 families are treated as vision-capable.
|
|
155
|
+
* - xAI: models containing `vision` in the name, plus grok-4 family, are treated as vision-capable.
|
|
156
|
+
* - Unknown providers: optimistically allowed (the API will reject if unsupported).
|
|
157
|
+
*/
|
|
158
|
+
function isVisionCapableModel(provider, model) {
|
|
159
|
+
switch (provider) {
|
|
160
|
+
case 'anthropic':
|
|
161
|
+
return true;
|
|
162
|
+
case 'openai': {
|
|
163
|
+
const m = model.toLowerCase();
|
|
164
|
+
if (m.startsWith('gpt-4o') || m.startsWith('gpt-4.1') || m.startsWith('gpt-4-turbo'))
|
|
165
|
+
return true;
|
|
166
|
+
if (m.startsWith('gpt-5'))
|
|
167
|
+
return true;
|
|
168
|
+
if (m.startsWith('o3') || m.startsWith('o4'))
|
|
169
|
+
return true;
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
case 'xai': {
|
|
173
|
+
const m = model.toLowerCase();
|
|
174
|
+
if (m.includes('vision'))
|
|
175
|
+
return true;
|
|
176
|
+
if (m.startsWith('grok-4'))
|
|
177
|
+
return true;
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
default:
|
|
181
|
+
// Unknown provider — optimistically allow (the API will reject if unsupported)
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Estimates the token count contribution of a single content part.
|
|
187
|
+
* For image parts, returns the flat IMAGE_TOKEN_ESTIMATE instead of serializing the binary data.
|
|
188
|
+
*/
|
|
189
|
+
function estimateContentPartTokens(part) {
|
|
190
|
+
if (typeof part !== 'object' || part === null || !('type' in part)) {
|
|
191
|
+
return Math.ceil(JSON.stringify(part).length / 3.6);
|
|
192
|
+
}
|
|
193
|
+
const typed = part;
|
|
194
|
+
if (typed.type === 'image' || typed.type === 'file') {
|
|
195
|
+
return exports.IMAGE_TOKEN_ESTIMATE;
|
|
196
|
+
}
|
|
197
|
+
if (typed.type === 'text' && typeof typed.text === 'string') {
|
|
198
|
+
return Math.ceil(typed.text.length / 3.6);
|
|
199
|
+
}
|
|
200
|
+
return Math.ceil(JSON.stringify(part).length / 3.6);
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Returns a new history array where `ImagePart` entries in user messages are replaced
|
|
204
|
+
* with a `[Image attached]` text placeholder. Does not mutate the original.
|
|
205
|
+
* Used before persisting history to disk to avoid writing base64 data.
|
|
206
|
+
*/
|
|
207
|
+
function stripImagesFromHistory(history) {
|
|
208
|
+
const hasImages = history.some((msg) => msg.role === 'user' &&
|
|
209
|
+
Array.isArray(msg.content) &&
|
|
210
|
+
msg.content.some((p) => typeof p === 'object' && p !== null && 'type' in p && p.type === 'image'));
|
|
211
|
+
if (!hasImages)
|
|
212
|
+
return history;
|
|
213
|
+
return history.map((msg) => {
|
|
214
|
+
if (msg.role !== 'user' || typeof msg.content === 'string' || !Array.isArray(msg.content)) {
|
|
215
|
+
return msg;
|
|
216
|
+
}
|
|
217
|
+
let changed = false;
|
|
218
|
+
const newContent = msg.content.map((part) => {
|
|
219
|
+
if (typeof part === 'object' && part !== null && 'type' in part && part.type === 'image') {
|
|
220
|
+
changed = true;
|
|
221
|
+
return { type: 'text', text: '[Image attached]' };
|
|
222
|
+
}
|
|
223
|
+
return part;
|
|
224
|
+
});
|
|
225
|
+
return changed ? { ...msg, content: newContent } : msg;
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
//# sourceMappingURL=image.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image.js","sourceRoot":"","sources":["../src/image.ts"],"names":[],"mappings":"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"}
|