@qvac/harness 0.7.3 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/CHANGELOG.md +45 -1
  2. package/NOTICE.md +16 -0
  3. package/dist-lib/lib/capabilities/completion.d.ts +1 -0
  4. package/dist-lib/lib/capabilities/completion.js +28 -8
  5. package/dist-lib/lib/capabilities/tool-turns.d.ts +3 -0
  6. package/dist-lib/lib/capabilities/tool-turns.js +7 -0
  7. package/dist-lib/lib/context-window.js +2 -2
  8. package/dist-lib/lib/helpers/run-outcome.d.ts +4 -1
  9. package/dist-lib/lib/helpers/run-outcome.js +14 -8
  10. package/dist-lib/lib/helpers/tool-call-markup.js +4 -4
  11. package/dist-lib/lib/hooks/built-in/arg-repair.js +7 -0
  12. package/dist-lib/lib/office/engine/codes.d.ts +62 -0
  13. package/dist-lib/lib/office/engine/codes.js +61 -0
  14. package/dist-lib/lib/office/engine/docx/document.d.ts +216 -0
  15. package/dist-lib/lib/office/engine/docx/document.js +1152 -0
  16. package/dist-lib/lib/office/engine/docx/index.d.ts +7 -0
  17. package/dist-lib/lib/office/engine/docx/index.js +8 -0
  18. package/dist-lib/lib/office/engine/docx/styles.d.ts +31 -0
  19. package/dist-lib/lib/office/engine/docx/styles.js +173 -0
  20. package/dist-lib/lib/office/engine/docx/template.d.ts +18 -0
  21. package/dist-lib/lib/office/engine/docx/template.js +133 -0
  22. package/dist-lib/lib/office/engine/docx/text.d.ts +55 -0
  23. package/dist-lib/lib/office/engine/docx/text.js +538 -0
  24. package/dist-lib/lib/office/engine/drawingml/chart.d.ts +63 -0
  25. package/dist-lib/lib/office/engine/drawingml/chart.js +539 -0
  26. package/dist-lib/lib/office/engine/drawingml/text.d.ts +35 -0
  27. package/dist-lib/lib/office/engine/drawingml/text.js +352 -0
  28. package/dist-lib/lib/office/engine/errors.d.ts +6 -0
  29. package/dist-lib/lib/office/engine/errors.js +12 -0
  30. package/dist-lib/lib/office/engine/image.d.ts +8 -0
  31. package/dist-lib/lib/office/engine/image.js +48 -0
  32. package/dist-lib/lib/office/engine/index.d.ts +12 -0
  33. package/dist-lib/lib/office/engine/index.js +7 -0
  34. package/dist-lib/lib/office/engine/opc/content-types.d.ts +17 -0
  35. package/dist-lib/lib/office/engine/opc/content-types.js +74 -0
  36. package/dist-lib/lib/office/engine/opc/media.d.ts +13 -0
  37. package/dist-lib/lib/office/engine/opc/media.js +11 -0
  38. package/dist-lib/lib/office/engine/opc/package.d.ts +51 -0
  39. package/dist-lib/lib/office/engine/opc/package.js +324 -0
  40. package/dist-lib/lib/office/engine/opc/paths.d.ts +7 -0
  41. package/dist-lib/lib/office/engine/opc/paths.js +46 -0
  42. package/dist-lib/lib/office/engine/opc/properties.d.ts +7 -0
  43. package/dist-lib/lib/office/engine/opc/properties.js +75 -0
  44. package/dist-lib/lib/office/engine/opc/relationships.d.ts +23 -0
  45. package/dist-lib/lib/office/engine/opc/relationships.js +65 -0
  46. package/dist-lib/lib/office/engine/opc/zip.d.ts +15 -0
  47. package/dist-lib/lib/office/engine/opc/zip.js +264 -0
  48. package/dist-lib/lib/office/engine/pptx/fit.d.ts +12 -0
  49. package/dist-lib/lib/office/engine/pptx/fit.js +37 -0
  50. package/dist-lib/lib/office/engine/pptx/index.d.ts +8 -0
  51. package/dist-lib/lib/office/engine/pptx/index.js +8 -0
  52. package/dist-lib/lib/office/engine/pptx/inherit.d.ts +29 -0
  53. package/dist-lib/lib/office/engine/pptx/inherit.js +155 -0
  54. package/dist-lib/lib/office/engine/pptx/notes.d.ts +5 -0
  55. package/dist-lib/lib/office/engine/pptx/notes.js +85 -0
  56. package/dist-lib/lib/office/engine/pptx/presentation.d.ts +54 -0
  57. package/dist-lib/lib/office/engine/pptx/presentation.js +749 -0
  58. package/dist-lib/lib/office/engine/pptx/read.d.ts +9 -0
  59. package/dist-lib/lib/office/engine/pptx/read.js +66 -0
  60. package/dist-lib/lib/office/engine/pptx/shapes.d.ts +52 -0
  61. package/dist-lib/lib/office/engine/pptx/shapes.js +276 -0
  62. package/dist-lib/lib/office/engine/pptx/slides.d.ts +27 -0
  63. package/dist-lib/lib/office/engine/pptx/slides.js +123 -0
  64. package/dist-lib/lib/office/engine/pptx/template.d.ts +20 -0
  65. package/dist-lib/lib/office/engine/pptx/template.js +391 -0
  66. package/dist-lib/lib/office/engine/pptx/types.d.ts +120 -0
  67. package/dist-lib/lib/office/engine/pptx/types.js +1 -0
  68. package/dist-lib/lib/office/engine/units.d.ts +3 -0
  69. package/dist-lib/lib/office/engine/units.js +12 -0
  70. package/dist-lib/lib/office/engine/utf8.d.ts +2 -0
  71. package/dist-lib/lib/office/engine/utf8.js +103 -0
  72. package/dist-lib/lib/office/engine/xlsx/formula.d.ts +49 -0
  73. package/dist-lib/lib/office/engine/xlsx/formula.js +381 -0
  74. package/dist-lib/lib/office/engine/xlsx/index.d.ts +8 -0
  75. package/dist-lib/lib/office/engine/xlsx/index.js +8 -0
  76. package/dist-lib/lib/office/engine/xlsx/refs.d.ts +22 -0
  77. package/dist-lib/lib/office/engine/xlsx/refs.js +82 -0
  78. package/dist-lib/lib/office/engine/xlsx/reshape.d.ts +21 -0
  79. package/dist-lib/lib/office/engine/xlsx/reshape.js +424 -0
  80. package/dist-lib/lib/office/engine/xlsx/sheet.d.ts +108 -0
  81. package/dist-lib/lib/office/engine/xlsx/sheet.js +662 -0
  82. package/dist-lib/lib/office/engine/xlsx/strings.d.ts +15 -0
  83. package/dist-lib/lib/office/engine/xlsx/strings.js +93 -0
  84. package/dist-lib/lib/office/engine/xlsx/styles.d.ts +38 -0
  85. package/dist-lib/lib/office/engine/xlsx/styles.js +369 -0
  86. package/dist-lib/lib/office/engine/xlsx/template.d.ts +7 -0
  87. package/dist-lib/lib/office/engine/xlsx/template.js +54 -0
  88. package/dist-lib/lib/office/engine/xlsx/workbook.d.ts +161 -0
  89. package/dist-lib/lib/office/engine/xlsx/workbook.js +850 -0
  90. package/dist-lib/lib/office/engine/xlsx/write.d.ts +2 -0
  91. package/dist-lib/lib/office/engine/xlsx/write.js +42 -0
  92. package/dist-lib/lib/office/engine/xml.d.ts +45 -0
  93. package/dist-lib/lib/office/engine/xml.js +258 -0
  94. package/dist-lib/lib/tools/exec/scope.d.ts +2 -0
  95. package/dist-lib/lib/tools/exec/scope.js +33 -2
  96. package/dist-lib/lib/tools/pdf/build.d.ts +43 -0
  97. package/dist-lib/lib/tools/pdf/build.js +150 -0
  98. package/dist-lib/lib/tools/pdf/images.d.ts +7 -0
  99. package/dist-lib/lib/tools/pdf/images.js +82 -0
  100. package/dist-lib/lib/tools/pdf/request.d.ts +63 -0
  101. package/dist-lib/lib/tools/pdf/request.js +335 -0
  102. package/dist-lib/lib/tools/pdf.d.ts +6 -0
  103. package/dist-lib/lib/tools/pdf.js +302 -0
  104. package/dist-lib/lib/tools/skill.js +14 -0
  105. package/package.json +25 -1
  106. package/worker/harness-sidecar.mjs +3 -1
@@ -0,0 +1,302 @@
1
+ import { errorCode, errorMessage } from '../guards.js';
2
+ import { checkOutputBytes, RUN_OUTPUT_BUDGET_BYTES } from './exec/adapters/python/output-policy.js';
3
+ import { build, pagesLabel, readDocs } from './pdf/build.js';
4
+ import { placeImages } from './pdf/images.js';
5
+ import { PAGE_SIZES, parseRequest, PDF_ACTIONS, WHERE_IDS_ARE } from './pdf/request.js';
6
+ export { parsePages, parseRequest } from './pdf/request.js';
7
+ const SUFFIXES = {
8
+ create: 'revised',
9
+ merge: 'merged',
10
+ split: 'pages',
11
+ rotate: 'rotated',
12
+ watermark: 'watermarked',
13
+ fill: 'filled',
14
+ encrypt: 'locked',
15
+ decrypt: 'unlocked',
16
+ text: 'text',
17
+ read: 'copy'
18
+ };
19
+ const attachedThisTurn = new WeakMap();
20
+ let loaded = null;
21
+ function loadPdfium() {
22
+ loaded ??= import('bare-pdfium').then((pdfium) => pdfium, () => null);
23
+ return loaded;
24
+ }
25
+ function stemOf(fileName) {
26
+ const base = fileName.replace(/^.*[\\/]/, '');
27
+ const dot = base.lastIndexOf('.');
28
+ return dot > 0 ? base.slice(0, dot) : base;
29
+ }
30
+ function slug(markdown) {
31
+ const heading = /^#{1,6}\s+(.+)$/m.exec(markdown)?.[1] ?? '';
32
+ const name = heading
33
+ .toLowerCase()
34
+ .replace(/[*_`[\]]/g, '')
35
+ .replace(/[^a-z0-9]+/g, '_')
36
+ .replace(/^_+|_+$/g, '')
37
+ .slice(0, 48);
38
+ return name || 'document';
39
+ }
40
+ function extensionOf(request) {
41
+ return request.action === 'text' ? 'txt' : 'pdf';
42
+ }
43
+ function defaultName(request, first) {
44
+ if (!first) {
45
+ return `${request.action === 'create' ? slug(request.markdown) : 'document'}.pdf`;
46
+ }
47
+ if (request.action === 'text') {
48
+ return `${stemOf(first.fileName)}.txt`;
49
+ }
50
+ return `${stemOf(first.fileName)}_${SUFFIXES[request.action]}.pdf`;
51
+ }
52
+ export function outputName(request, inputs) {
53
+ const extension = extensionOf(request);
54
+ const { output } = request;
55
+ let wanted = defaultName(request, inputs[0]);
56
+ if (output && /\.[a-z0-9]{2,4}$/i.test(output)) {
57
+ wanted = output.replace(/\.pdf$/i, `.${extension}`);
58
+ }
59
+ else if (output) {
60
+ wanted = `${output}.${extension}`;
61
+ }
62
+ const taken = inputs.some((input) => input.fileName.toLowerCase() === wanted.toLowerCase());
63
+ return taken ? `${stemOf(wanted)}_${SUFFIXES[request.action]}.${extension}` : wanted;
64
+ }
65
+ async function fetchSources(ids, ctx) {
66
+ if (!ctx.fetchAttachment) {
67
+ return { error: 'this device cannot read chat attachments' };
68
+ }
69
+ const sources = [];
70
+ for (const attachmentId of ids) {
71
+ const found = await ctx.fetchAttachment({ attachmentId, signal: ctx.signal });
72
+ if (!found || !('bytes' in found)) {
73
+ return { error: `attachment ${attachmentId} not found in this chat; ${WHERE_IDS_ARE}` };
74
+ }
75
+ sources.push({ attachmentId, fileName: found.fileName, bytes: found.bytes });
76
+ }
77
+ return sources;
78
+ }
79
+ function openError(err, source, password) {
80
+ const code = errorCode(err);
81
+ if (code === 'PASSWORD' && password) {
82
+ return `the password is wrong for ${source.fileName}; ask the user for the right one, never guess`;
83
+ }
84
+ if (code === 'PASSWORD') {
85
+ return `${source.fileName} is locked; ask the user for its password and pass it as password`;
86
+ }
87
+ if (code === 'FORMAT' || code === 'FILE') {
88
+ return `${source.fileName} is not a PDF that opens`;
89
+ }
90
+ return `${source.fileName}: ${errorMessage(err)}`;
91
+ }
92
+ function openAll(pdfium, sources, request) {
93
+ const password = request.action === 'encrypt' ? undefined : request.password;
94
+ const opened = [];
95
+ for (const source of sources) {
96
+ try {
97
+ opened.push({ source, doc: pdfium.open(source.bytes, { password }) });
98
+ }
99
+ catch (err) {
100
+ return { opened, error: openError(err, source, password) };
101
+ }
102
+ }
103
+ return { opened, error: null };
104
+ }
105
+ async function attachOutput(fileName, bytes, ctx) {
106
+ if (!ctx.attach) {
107
+ return { error: 'this device cannot attach a file to the chat' };
108
+ }
109
+ const delivered = attachedThisTurn.get(ctx.attach) ?? new Map();
110
+ const earlier = delivered.get(fileName.toLowerCase());
111
+ if (earlier) {
112
+ return { attachment: earlier, repeated: true };
113
+ }
114
+ const verdict = checkOutputBytes(fileName, bytes, 0);
115
+ if ('error' in verdict) {
116
+ return verdict;
117
+ }
118
+ const budget = ctx.runBudget;
119
+ if (budget && budget.outputBytesUsed + bytes.byteLength > RUN_OUTPUT_BUDGET_BYTES) {
120
+ return { error: 'run output budget exceeded' };
121
+ }
122
+ const record = await ctx.attach({ fileName, mimeType: verdict.mimeType, bytes, raw: true });
123
+ if (budget) {
124
+ budget.outputBytesUsed += bytes.byteLength;
125
+ }
126
+ const attachment = {
127
+ attachmentId: record.id,
128
+ fileName: record.fileName,
129
+ byteLength: record.byteLength
130
+ };
131
+ delivered.set(fileName.toLowerCase(), attachment);
132
+ attachedThisTurn.set(ctx.attach, delivered);
133
+ return { attachment, repeated: false };
134
+ }
135
+ const UNDRAWABLE = /^('[^']+' \(U\+[0-9A-F]+\)(?:, '[^']+' \(U\+[0-9A-F]+\))*) cannot be drawn by the built-in fonts/;
136
+ function buildError(err) {
137
+ const message = errorMessage(err);
138
+ const characters = UNDRAWABLE.exec(message)?.[1];
139
+ if (!characters) {
140
+ return message;
141
+ }
142
+ return (`${characters} cannot be drawn: a PDF made here takes Western European text only. ` +
143
+ 'Leave those characters out, or tell the user this text cannot go in a PDF');
144
+ }
145
+ function buildSafely({ pdfium, request, opened }) {
146
+ try {
147
+ return build(pdfium, request, opened);
148
+ }
149
+ catch (err) {
150
+ return buildError(err);
151
+ }
152
+ }
153
+ function reply(request, { output, built, delivered }) {
154
+ const { attachment, repeated } = delivered;
155
+ const summary = request.action === 'text'
156
+ ? `text of ${pagesLabel(built.pageCount)}`
157
+ : pagesLabel(built.pageCount);
158
+ const renamed = request.output && output !== request.output
159
+ ? `Saved as ${output}, so the attached file keeps its own name. `
160
+ : '';
161
+ return {
162
+ attachments: [attachment],
163
+ pageCount: built.pageCount,
164
+ reply: `${attachment.fileName} — ${summary}`,
165
+ next: renamed +
166
+ `Done: ${attachment.fileName} ${repeated ? 'was already' : 'is'} attached to the chat and shown to the user. ` +
167
+ 'Do not call pdf again for it. Your answer is the reply value alone, word for word.'
168
+ };
169
+ }
170
+ async function deliver(job, ctx) {
171
+ const { request, opened } = job;
172
+ const output = outputName(request, opened.map((one) => one.source));
173
+ const extension = `.${extensionOf(request)}`;
174
+ if (!output.toLowerCase().endsWith(extension)) {
175
+ return { error: `output must end in ${extension}, got "${output}"` };
176
+ }
177
+ const built = buildSafely(job);
178
+ if (typeof built === 'string') {
179
+ return { error: built };
180
+ }
181
+ const delivered = await attachOutput(output, built.bytes, ctx);
182
+ return 'error' in delivered ? delivered : reply(request, { output, built, delivered });
183
+ }
184
+ function isLocked(pdfium, source) {
185
+ try {
186
+ pdfium.open(source.bytes).close();
187
+ return false;
188
+ }
189
+ catch (err) {
190
+ return errorCode(err) === 'PASSWORD';
191
+ }
192
+ }
193
+ function lockOrUnlock(pdfium, request, sources) {
194
+ const [only] = sources;
195
+ const wantsFile = request.output?.toLowerCase().endsWith('.pdf') === true;
196
+ if (request.action !== 'read' || !request.password || !wantsFile || !only || sources.length > 1) {
197
+ return request;
198
+ }
199
+ if (isLocked(pdfium, only)) {
200
+ return { ...request, action: 'decrypt' };
201
+ }
202
+ return { ...request, action: 'encrypt', ownerPassword: undefined, permissions: undefined };
203
+ }
204
+ async function run(pdfium, asked, ctx) {
205
+ if (asked.action === 'create') {
206
+ const placed = await placeImages(asked, ctx);
207
+ return 'error' in placed ? placed : deliver({ pdfium, request: placed, opened: [] }, ctx);
208
+ }
209
+ const sources = asked.ids.length ? await fetchSources(asked.ids, ctx) : [];
210
+ if ('error' in sources) {
211
+ return sources;
212
+ }
213
+ const request = lockOrUnlock(pdfium, asked, sources);
214
+ const { opened, error } = openAll(pdfium, sources, request);
215
+ try {
216
+ if (error) {
217
+ return { error };
218
+ }
219
+ if (request.action === 'read') {
220
+ return readDocs(opened);
221
+ }
222
+ return await deliver({ pdfium, request, opened }, ctx);
223
+ }
224
+ finally {
225
+ for (const { doc } of opened) {
226
+ doc.close();
227
+ }
228
+ }
229
+ }
230
+ const DESCRIPTION = 'Create, read or change PDF files. With attachmentId and no action it returns the page ' +
231
+ 'count, each page’s text and the form fields (name, type, value, options). An action saves ' +
232
+ 'a new file named output and attaches it to the chat: create (markdown, where ' +
233
+ '![caption](attachmentId) on its own line places a chat image and the file name of the ' +
234
+ "user's upload places that upload; optional pageSize, " +
235
+ 'landscape, pageNumbers, password), merge (attachmentIds in order), split (pages), rotate ' +
236
+ '(degrees 90, 180 or 270, optional pages), watermark (text, optional opacity, position, ' +
237
+ 'pages), fill (values keyed by field name), encrypt (password, optional ownerPassword, ' +
238
+ 'permissions), decrypt (password), text (a .txt of the text). Pages count from 1. A locked ' +
239
+ 'PDF needs its password on every action. A failing call saves nothing and its error names ' +
240
+ 'what exists, so fix the call from it.';
241
+ const PARAMETERS = {
242
+ type: 'object',
243
+ properties: {
244
+ action: {
245
+ type: 'string',
246
+ enum: [...PDF_ACTIONS],
247
+ description: 'What to do; leave it out to read the PDF'
248
+ },
249
+ attachmentId: { type: 'string', description: 'The PDF in this chat' },
250
+ attachmentIds: {
251
+ type: 'array',
252
+ items: { type: 'string' },
253
+ description: 'merge: the PDFs to join, in order'
254
+ },
255
+ markdown: { type: 'string', description: 'create: the whole document as Markdown' },
256
+ pages: {
257
+ type: 'array',
258
+ items: { type: 'integer' },
259
+ description: 'Page numbers counted from 1'
260
+ },
261
+ degrees: { type: 'integer', description: 'rotate: 90, 180 or 270, clockwise' },
262
+ text: { type: 'string', description: 'watermark: the text to stamp' },
263
+ opacity: { type: 'number', description: 'watermark: 0 to 1' },
264
+ position: { type: 'string', enum: ['over', 'under'], description: 'watermark' },
265
+ values: { type: 'object', description: 'fill: { "field name": value }' },
266
+ password: {
267
+ type: 'string',
268
+ description: 'encrypt: the new password; otherwise the password of a locked PDF'
269
+ },
270
+ ownerPassword: { type: 'string', description: 'encrypt' },
271
+ permissions: { type: 'object', description: 'encrypt: { print, copy, modify, ... }' },
272
+ pageSize: { type: 'string', enum: [...PAGE_SIZES], description: 'create' },
273
+ landscape: { type: 'boolean', description: 'create' },
274
+ pageNumbers: { type: 'boolean', description: 'create' },
275
+ output: { type: 'string', description: 'File name for the saved file' }
276
+ }
277
+ };
278
+ export function createPdfTool(load = loadPdfium) {
279
+ return {
280
+ name: 'pdf',
281
+ toolset: null,
282
+ skillGated: true,
283
+ description: DESCRIPTION,
284
+ parameters: PARAMETERS,
285
+ effect: () => 'read',
286
+ validate(args) {
287
+ const request = parseRequest(args);
288
+ return typeof request === 'string' ? request : null;
289
+ },
290
+ async execute(args, ctx = {}) {
291
+ const request = parseRequest(args);
292
+ if (typeof request === 'string') {
293
+ return { error: request };
294
+ }
295
+ const pdfium = await load();
296
+ if (!pdfium) {
297
+ return { error: 'this device cannot open PDFs' };
298
+ }
299
+ return run(pdfium, request, ctx);
300
+ }
301
+ };
302
+ }
@@ -158,6 +158,14 @@ export function createSkillTool({ skillsDir, overlayDir = resolveSkillsOverlayDi
158
158
  content: notes.length ? [content.trim(), ...notes].join('\n\n') : content
159
159
  };
160
160
  };
161
+ const mainInstead = async (asked, available) => {
162
+ const main = await load(MAIN_FILE);
163
+ const others = available.length ? ` Its other files: ${available.join(', ')}.` : '';
164
+ return {
165
+ ...main,
166
+ content: `There is no file ${asked} in this skill; these are its instructions.${others}\n\n${main.content}`
167
+ };
168
+ };
161
169
  try {
162
170
  return await load(file);
163
171
  }
@@ -173,6 +181,12 @@ export function createSkillTool({ skillsDir, overlayDir = resolveSkillsOverlayDi
173
181
  }
174
182
  catch { }
175
183
  }
184
+ if (!ctx?.skillsLoadedThisTurn?.includes(name)) {
185
+ try {
186
+ return await mainInstead(file, available);
187
+ }
188
+ catch { }
189
+ }
176
190
  const rest = available.length
177
191
  ? ` — files this skill has: ${available.join(', ')}`
178
192
  : '. This skill has no other files; you already have its full instructions — act now.';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qvac/harness",
3
- "version": "0.7.3",
3
+ "version": "0.8.0",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "bare": ">=1.34.0"
@@ -478,6 +478,22 @@
478
478
  "types": "./dist-lib/lib/tools/outbound-guard.d.ts",
479
479
  "default": "./dist-lib/lib/tools/outbound-guard.js"
480
480
  },
481
+ "./lib/tools/pdf.ts": {
482
+ "types": "./dist-lib/lib/tools/pdf.d.ts",
483
+ "default": "./dist-lib/lib/tools/pdf.js"
484
+ },
485
+ "./lib/tools/pdf/build.ts": {
486
+ "types": "./dist-lib/lib/tools/pdf/build.d.ts",
487
+ "default": "./dist-lib/lib/tools/pdf/build.js"
488
+ },
489
+ "./lib/tools/pdf/images.ts": {
490
+ "types": "./dist-lib/lib/tools/pdf/images.d.ts",
491
+ "default": "./dist-lib/lib/tools/pdf/images.js"
492
+ },
493
+ "./lib/tools/pdf/request.ts": {
494
+ "types": "./dist-lib/lib/tools/pdf/request.d.ts",
495
+ "default": "./dist-lib/lib/tools/pdf/request.js"
496
+ },
481
497
  "./lib/tools/private-ip.ts": {
482
498
  "types": "./dist-lib/lib/tools/private-ip.d.ts",
483
499
  "default": "./dist-lib/lib/tools/private-ip.js"
@@ -716,6 +732,11 @@
716
732
  "types": "@qvac/sdk",
717
733
  "bare": "@qvac/inference",
718
734
  "default": "@qvac/sdk"
735
+ },
736
+ "#zlib": {
737
+ "types": "bare-zlib",
738
+ "bare": "bare-zlib",
739
+ "default": "zlib"
719
740
  }
720
741
  },
721
742
  "dependencies": {
@@ -723,6 +744,7 @@
723
744
  "@qvac/langdetect-text": "0.1.2",
724
745
  "@qvac/sdk": "0.20.0",
725
746
  "@qvac/skills": "0.1.9",
747
+ "@xmldom/xmldom": "^0.9.12",
726
748
  "asenion": "^0.1.0",
727
749
  "b4a": "^1.9.0",
728
750
  "bare-abort-controller": "^1.1.2",
@@ -743,6 +765,7 @@
743
765
  "bare-net": "^2.3.2",
744
766
  "bare-os": "^3.9.1",
745
767
  "bare-path": "^3.0.1",
768
+ "bare-pdfium": "^1.6.0",
746
769
  "bare-process": "4.4.1",
747
770
  "bare-sidecar": "0.5.7",
748
771
  "bare-storage": "^1.1.0",
@@ -754,6 +777,7 @@
754
777
  "bare-tty": "^5.1.2",
755
778
  "bare-url": "^2.4.5",
756
779
  "bare-worker": "^4.5.0",
780
+ "bare-zlib": "^1.4.1",
757
781
  "compact-encoding": "^3.4.0",
758
782
  "hrpc": "^4.3.0",
759
783
  "hyperschema": "^1.23.0",
@@ -21,6 +21,7 @@ import { createGrepTool } from '../dist-lib/lib/tools/grep.js'
21
21
  import { createHttpRequestTool } from '../dist-lib/lib/tools/http-request.js'
22
22
  import { createMcpCallTool } from '../dist-lib/lib/tools/mcp-call.js'
23
23
  import { compileOperationTools } from '../dist-lib/lib/tools/operations/compile.js'
24
+ import { createPdfTool } from '../dist-lib/lib/tools/pdf.js'
24
25
  import { createReadFilesTool } from '../dist-lib/lib/tools/read-files.js'
25
26
  import { createSkillTool } from '../dist-lib/lib/tools/skill.js'
26
27
  import { ensureBundledSkillsDir } from '../dist-lib/lib/tools/skills/bundled-dir.js'
@@ -132,7 +133,8 @@ export default async function start(ipc, ready) {
132
133
  createFileGlobTool({ deniedRoots }),
133
134
  createGrepTool({ deniedRoots }),
134
135
  createCreateDocumentsTool({ deniedRoots }),
135
- createEditDocumentsTool({ deniedRoots })
136
+ createEditDocumentsTool({ deniedRoots }),
137
+ createPdfTool()
136
138
  ]
137
139
  if (skillCatalog) {
138
140
  tools.push(...compileOperationTools(skillCatalog, { httpRequestTool, mcpCallTool }))