agent-nuvira 3.3.3 → 3.3.5
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 +13 -5
- package/dist/agent-sdk/src/agent.d.ts +2 -0
- package/dist/agent-sdk/src/agent.d.ts.map +1 -1
- package/dist/agent-sdk/src/define.d.ts +64 -0
- package/dist/agent-sdk/src/define.d.ts.map +1 -0
- package/dist/agent-sdk/src/define.js +76 -0
- package/dist/agent-sdk/src/define.js.map +1 -0
- package/dist/agent-sdk/src/index.d.ts +9 -0
- package/dist/agent-sdk/src/index.d.ts.map +1 -1
- package/dist/agent-sdk/src/index.js +9 -0
- package/dist/agent-sdk/src/index.js.map +1 -1
- package/dist/agent-sdk/src/scaffold.d.ts +11 -0
- package/dist/agent-sdk/src/scaffold.d.ts.map +1 -1
- package/dist/agent-sdk/src/scaffold.js +16 -6
- package/dist/agent-sdk/src/scaffold.js.map +1 -1
- package/dist/agents/long-form-plan.d.ts.map +1 -1
- package/dist/agents/long-form-plan.js +2 -1
- package/dist/agents/long-form-plan.js.map +1 -1
- package/dist/agents/orchestrator.d.ts.map +1 -1
- package/dist/agents/orchestrator.js +5 -4
- package/dist/agents/orchestrator.js.map +1 -1
- package/dist/cli/agent.d.ts +2 -2
- package/dist/cli/agent.js +10 -10
- package/dist/cli/chat.d.ts +94 -0
- package/dist/cli/chat.d.ts.map +1 -1
- package/dist/cli/chat.js +354 -20
- package/dist/cli/chat.js.map +1 -1
- package/dist/cli/cli-program.d.ts.map +1 -1
- package/dist/cli/cli-program.js +5 -0
- package/dist/cli/cli-program.js.map +1 -1
- package/dist/cli/config.d.ts.map +1 -1
- package/dist/cli/config.js +9 -1
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +3 -2
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/edit.js +2 -2
- package/dist/cli/eval.d.ts +17 -0
- package/dist/cli/eval.d.ts.map +1 -1
- package/dist/cli/eval.js +105 -2
- package/dist/cli/eval.js.map +1 -1
- package/dist/cli/execute.d.ts +16 -1
- package/dist/cli/execute.d.ts.map +1 -1
- package/dist/cli/execute.js +192 -22
- package/dist/cli/execute.js.map +1 -1
- package/dist/cli/loop-executor.d.ts +55 -0
- package/dist/cli/loop-executor.d.ts.map +1 -1
- package/dist/cli/loop-executor.js +212 -13
- package/dist/cli/loop-executor.js.map +1 -1
- package/dist/cli/model.d.ts.map +1 -1
- package/dist/cli/model.js +9 -8
- package/dist/cli/model.js.map +1 -1
- package/dist/cli/models.d.ts +1 -1
- package/dist/cli/models.js +4 -4
- package/dist/cli/parity.d.ts +85 -0
- package/dist/cli/parity.d.ts.map +1 -0
- package/dist/cli/parity.js +506 -0
- package/dist/cli/parity.js.map +1 -0
- package/dist/cli/plan.d.ts.map +1 -1
- package/dist/cli/plan.js +2 -1
- package/dist/cli/plan.js.map +1 -1
- package/dist/cli/retrieval.d.ts.map +1 -1
- package/dist/cli/retrieval.js +5 -4
- package/dist/cli/retrieval.js.map +1 -1
- package/dist/cli/sdk.js +4 -4
- package/dist/cli/sdk.js.map +1 -1
- package/dist/cli/trace.d.ts.map +1 -1
- package/dist/cli/trace.js +2 -1
- package/dist/cli/trace.js.map +1 -1
- package/dist/cli/workflow.js +2 -2
- package/dist/cli/workflow.js.map +1 -1
- package/dist/config/process-env.d.ts +136 -0
- package/dist/config/process-env.d.ts.map +1 -0
- package/dist/config/process-env.js +217 -0
- package/dist/config/process-env.js.map +1 -0
- package/dist/config/types.d.ts +44 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/findings/verdicts.d.ts +241 -0
- package/dist/findings/verdicts.d.ts.map +1 -0
- package/dist/findings/verdicts.js +284 -0
- package/dist/findings/verdicts.js.map +1 -0
- package/dist/gateway/adapters.d.ts +65 -0
- package/dist/gateway/adapters.d.ts.map +1 -1
- package/dist/gateway/adapters.js +216 -10
- package/dist/gateway/adapters.js.map +1 -1
- package/dist/gateway/channel-directory.d.ts +31 -0
- package/dist/gateway/channel-directory.d.ts.map +1 -1
- package/dist/gateway/channel-directory.js +40 -0
- package/dist/gateway/channel-directory.js.map +1 -1
- package/dist/gateway/gateway-log.d.ts +1 -1
- package/dist/gateway/gateway-log.d.ts.map +1 -1
- package/dist/gateway/gateway-log.js.map +1 -1
- package/dist/gateway/hooks.d.ts +87 -19
- package/dist/gateway/hooks.d.ts.map +1 -1
- package/dist/gateway/hooks.js +62 -23
- package/dist/gateway/hooks.js.map +1 -1
- package/dist/gateway/inbound-media.d.ts +147 -0
- package/dist/gateway/inbound-media.d.ts.map +1 -0
- package/dist/gateway/inbound-media.js +317 -0
- package/dist/gateway/inbound-media.js.map +1 -0
- package/dist/gateway/inbox.d.ts +8 -1
- package/dist/gateway/inbox.d.ts.map +1 -1
- package/dist/gateway/inbox.js.map +1 -1
- package/dist/gateway/platform-config.d.ts +14 -0
- package/dist/gateway/platform-config.d.ts.map +1 -1
- package/dist/gateway/platform-config.js +26 -8
- package/dist/gateway/platform-config.js.map +1 -1
- package/dist/gateway/realtime.d.ts +114 -0
- package/dist/gateway/realtime.d.ts.map +1 -0
- package/dist/gateway/realtime.js +402 -0
- package/dist/gateway/realtime.js.map +1 -0
- package/dist/gateway/registry.d.ts +31 -0
- package/dist/gateway/registry.d.ts.map +1 -1
- package/dist/gateway/registry.js +224 -4
- package/dist/gateway/registry.js.map +1 -1
- package/dist/gateway/whatsapp/baileys-bridge.d.ts +11 -1
- package/dist/gateway/whatsapp/baileys-bridge.d.ts.map +1 -1
- package/dist/gateway/whatsapp/baileys-bridge.js +123 -4
- package/dist/gateway/whatsapp/baileys-bridge.js.map +1 -1
- package/dist/gateway/whatsapp/bridge.d.ts +6 -2
- package/dist/gateway/whatsapp/bridge.d.ts.map +1 -1
- package/dist/gateway/whatsapp/bridge.js.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/inference/factory.d.ts +14 -0
- package/dist/inference/factory.d.ts.map +1 -1
- package/dist/inference/factory.js +17 -0
- package/dist/inference/factory.js.map +1 -1
- package/dist/inference/groq-adapter.d.ts +2 -0
- package/dist/inference/groq-adapter.d.ts.map +1 -1
- package/dist/inference/groq-adapter.js +16 -6
- package/dist/inference/groq-adapter.js.map +1 -1
- package/dist/inference/tools.d.ts.map +1 -1
- package/dist/inference/tools.js +29 -0
- package/dist/inference/tools.js.map +1 -1
- package/dist/learning/benchmark.d.ts.map +1 -1
- package/dist/learning/benchmark.js +3 -2
- package/dist/learning/benchmark.js.map +1 -1
- package/dist/learning/continuation.d.ts.map +1 -1
- package/dist/learning/continuation.js +2 -1
- package/dist/learning/continuation.js.map +1 -1
- package/dist/learning/cost-tracker.d.ts.map +1 -1
- package/dist/learning/cost-tracker.js +2 -1
- package/dist/learning/cost-tracker.js.map +1 -1
- package/dist/learning/deferred-task.d.ts.map +1 -1
- package/dist/learning/deferred-task.js +13 -4
- package/dist/learning/deferred-task.js.map +1 -1
- package/dist/learning/eval-framework.d.ts.map +1 -1
- package/dist/learning/eval-framework.js +2 -1
- package/dist/learning/eval-framework.js.map +1 -1
- package/dist/learning/long-form.d.ts.map +1 -1
- package/dist/learning/long-form.js +2 -1
- package/dist/learning/long-form.js.map +1 -1
- package/dist/learning/model-reachability.d.ts +95 -0
- package/dist/learning/model-reachability.d.ts.map +1 -0
- package/dist/learning/model-reachability.js +111 -0
- package/dist/learning/model-reachability.js.map +1 -0
- package/dist/learning/model-registry.d.ts +22 -0
- package/dist/learning/model-registry.d.ts.map +1 -1
- package/dist/learning/model-registry.js +26 -1
- package/dist/learning/model-registry.js.map +1 -1
- package/dist/learning/model-verify-job.d.ts +131 -0
- package/dist/learning/model-verify-job.d.ts.map +1 -0
- package/dist/learning/model-verify-job.js +221 -0
- package/dist/learning/model-verify-job.js.map +1 -0
- package/dist/learning/reasoning-cache.d.ts.map +1 -1
- package/dist/learning/reasoning-cache.js +2 -1
- package/dist/learning/reasoning-cache.js.map +1 -1
- package/dist/learning/reasoning-trace.d.ts +37 -1
- package/dist/learning/reasoning-trace.d.ts.map +1 -1
- package/dist/learning/reasoning-trace.js +66 -0
- package/dist/learning/reasoning-trace.js.map +1 -1
- package/dist/learning/resilient-call.d.ts.map +1 -1
- package/dist/learning/resilient-call.js +2 -1
- package/dist/learning/resilient-call.js.map +1 -1
- package/dist/learning/retrieval.d.ts.map +1 -1
- package/dist/learning/retrieval.js +2 -1
- package/dist/learning/retrieval.js.map +1 -1
- package/dist/learning/seeded-benchmark.d.ts +160 -0
- package/dist/learning/seeded-benchmark.d.ts.map +1 -0
- package/dist/learning/seeded-benchmark.js +321 -0
- package/dist/learning/seeded-benchmark.js.map +1 -0
- package/dist/learning/seeded-bugs.d.ts +142 -0
- package/dist/learning/seeded-bugs.d.ts.map +1 -0
- package/dist/learning/seeded-bugs.js +535 -0
- package/dist/learning/seeded-bugs.js.map +1 -0
- package/dist/learning/step-checkpoint.d.ts +127 -0
- package/dist/learning/step-checkpoint.d.ts.map +1 -0
- package/dist/learning/step-checkpoint.js +244 -0
- package/dist/learning/step-checkpoint.js.map +1 -0
- package/dist/nlu/intent-confirm.d.ts +23 -1
- package/dist/nlu/intent-confirm.d.ts.map +1 -1
- package/dist/nlu/intent-confirm.js +85 -2
- package/dist/nlu/intent-confirm.js.map +1 -1
- package/dist/nlu/learnings.d.ts +7 -0
- package/dist/nlu/learnings.d.ts.map +1 -1
- package/dist/nlu/learnings.js +7 -0
- package/dist/nlu/learnings.js.map +1 -1
- package/dist/observability/debug-log.d.ts +250 -0
- package/dist/observability/debug-log.d.ts.map +1 -0
- package/dist/observability/debug-log.js +500 -0
- package/dist/observability/debug-log.js.map +1 -0
- package/dist/observability/event-bus.d.ts.map +1 -1
- package/dist/observability/event-bus.js +4 -1
- package/dist/observability/event-bus.js.map +1 -1
- package/dist/observability/otel.d.ts +278 -0
- package/dist/observability/otel.d.ts.map +1 -0
- package/dist/observability/otel.js +590 -0
- package/dist/observability/otel.js.map +1 -0
- package/dist/parity/drivers.d.ts +99 -0
- package/dist/parity/drivers.d.ts.map +1 -0
- package/dist/parity/drivers.js +1362 -0
- package/dist/parity/drivers.js.map +1 -0
- package/dist/parity/graph.d.ts +73 -0
- package/dist/parity/graph.d.ts.map +1 -0
- package/dist/parity/graph.js +162 -0
- package/dist/parity/graph.js.map +1 -0
- package/dist/parity/matrix.d.ts +105 -0
- package/dist/parity/matrix.d.ts.map +1 -0
- package/dist/parity/matrix.js +352 -0
- package/dist/parity/matrix.js.map +1 -0
- package/dist/parity/observation.d.ts +444 -0
- package/dist/parity/observation.d.ts.map +1 -0
- package/dist/parity/observation.js +333 -0
- package/dist/parity/observation.js.map +1 -0
- package/dist/parity/scenarios.d.ts +229 -0
- package/dist/parity/scenarios.d.ts.map +1 -0
- package/dist/parity/scenarios.js +175 -0
- package/dist/parity/scenarios.js.map +1 -0
- package/dist/parity/surfaces.d.ts +122 -0
- package/dist/parity/surfaces.d.ts.map +1 -0
- package/dist/parity/surfaces.js +190 -0
- package/dist/parity/surfaces.js.map +1 -0
- package/dist/runtime/fault-injection.d.ts +173 -0
- package/dist/runtime/fault-injection.d.ts.map +1 -0
- package/dist/runtime/fault-injection.js +281 -0
- package/dist/runtime/fault-injection.js.map +1 -0
- package/dist/tools/child-agent-entry.d.ts +23 -0
- package/dist/tools/child-agent-entry.d.ts.map +1 -0
- package/dist/tools/child-agent-entry.js +129 -0
- package/dist/tools/child-agent-entry.js.map +1 -0
- package/dist/tools/child-agent-runtime.d.ts +124 -0
- package/dist/tools/child-agent-runtime.d.ts.map +1 -0
- package/dist/tools/child-agent-runtime.js +704 -0
- package/dist/tools/child-agent-runtime.js.map +1 -0
- package/dist/tools/coding-tools.d.ts.map +1 -1
- package/dist/tools/coding-tools.js +82 -10
- package/dist/tools/coding-tools.js.map +1 -1
- package/dist/tools/delegation-system.d.ts +31 -0
- package/dist/tools/delegation-system.d.ts.map +1 -1
- package/dist/tools/delegation-system.js +70 -9
- package/dist/tools/delegation-system.js.map +1 -1
- package/dist/tools/extract/docx.d.ts +27 -0
- package/dist/tools/extract/docx.d.ts.map +1 -0
- package/dist/tools/extract/docx.js +48 -0
- package/dist/tools/extract/docx.js.map +1 -0
- package/dist/tools/extract/html-text.d.ts +27 -0
- package/dist/tools/extract/html-text.d.ts.map +1 -0
- package/dist/tools/extract/html-text.js +86 -0
- package/dist/tools/extract/html-text.js.map +1 -0
- package/dist/tools/extract/pdf-ocr.d.ts +58 -0
- package/dist/tools/extract/pdf-ocr.d.ts.map +1 -0
- package/dist/tools/extract/pdf-ocr.js +116 -0
- package/dist/tools/extract/pdf-ocr.js.map +1 -0
- package/dist/tools/extract/pdf.d.ts +65 -0
- package/dist/tools/extract/pdf.d.ts.map +1 -0
- package/dist/tools/extract/pdf.js +197 -0
- package/dist/tools/extract/pdf.js.map +1 -0
- package/dist/tools/extract/pptx.d.ts +32 -0
- package/dist/tools/extract/pptx.d.ts.map +1 -0
- package/dist/tools/extract/pptx.js +77 -0
- package/dist/tools/extract/pptx.js.map +1 -0
- package/dist/tools/extract/xlsx.d.ts +47 -0
- package/dist/tools/extract/xlsx.d.ts.map +1 -0
- package/dist/tools/extract/xlsx.js +111 -0
- package/dist/tools/extract/xlsx.js.map +1 -0
- package/dist/tools/finding-tool.d.ts +76 -0
- package/dist/tools/finding-tool.d.ts.map +1 -0
- package/dist/tools/finding-tool.js +125 -0
- package/dist/tools/finding-tool.js.map +1 -0
- package/dist/tools/messaging-tools.d.ts +41 -11
- package/dist/tools/messaging-tools.d.ts.map +1 -1
- package/dist/tools/messaging-tools.js +104 -55
- package/dist/tools/messaging-tools.js.map +1 -1
- package/dist/tools/neutts-synth.d.ts +27 -3
- package/dist/tools/neutts-synth.d.ts.map +1 -1
- package/dist/tools/neutts-synth.js +57 -13
- package/dist/tools/neutts-synth.js.map +1 -1
- package/dist/tools/pipeline-tool.d.ts +43 -1
- package/dist/tools/pipeline-tool.d.ts.map +1 -1
- package/dist/tools/pipeline-tool.js +13 -2
- package/dist/tools/pipeline-tool.js.map +1 -1
- package/dist/tools/read-extract.d.ts +116 -45
- package/dist/tools/read-extract.d.ts.map +1 -1
- package/dist/tools/read-extract.js +494 -158
- package/dist/tools/read-extract.js.map +1 -1
- package/dist/tools/registry.d.ts +2 -2
- package/dist/tools/registry.d.ts.map +1 -1
- package/dist/tools/registry.js +152 -23
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/subagent-refusal.d.ts +15 -0
- package/dist/tools/subagent-refusal.d.ts.map +1 -0
- package/dist/tools/subagent-refusal.js +18 -0
- package/dist/tools/subagent-refusal.js.map +1 -0
- package/dist/tools/subagent-spawner.d.ts +122 -0
- package/dist/tools/subagent-spawner.d.ts.map +1 -1
- package/dist/tools/subagent-spawner.js +249 -28
- package/dist/tools/subagent-spawner.js.map +1 -1
- package/dist/tools/tool-hooks.d.ts +177 -0
- package/dist/tools/tool-hooks.d.ts.map +1 -0
- package/dist/tools/tool-hooks.js +427 -0
- package/dist/tools/tool-hooks.js.map +1 -0
- package/dist/tools/tool-loop.d.ts +82 -0
- package/dist/tools/tool-loop.d.ts.map +1 -1
- package/dist/tools/tool-loop.js +167 -9
- package/dist/tools/tool-loop.js.map +1 -1
- package/dist/tools/tool-refusal.d.ts +68 -0
- package/dist/tools/tool-refusal.d.ts.map +1 -0
- package/dist/tools/tool-refusal.js +78 -0
- package/dist/tools/tool-refusal.js.map +1 -0
- package/dist/tools/toolsets.d.ts +8 -0
- package/dist/tools/toolsets.d.ts.map +1 -1
- package/dist/tools/toolsets.js +12 -2
- package/dist/tools/toolsets.js.map +1 -1
- package/dist/tools/vision-tools.d.ts +88 -83
- package/dist/tools/vision-tools.d.ts.map +1 -1
- package/dist/tools/vision-tools.js +134 -103
- package/dist/tools/vision-tools.js.map +1 -1
- package/dist/tools/worktree.d.ts +210 -0
- package/dist/tools/worktree.d.ts.map +1 -0
- package/dist/tools/worktree.js +374 -0
- package/dist/tools/worktree.js.map +1 -0
- package/dist/utils/format.d.ts +3 -0
- package/dist/utils/format.d.ts.map +1 -0
- package/dist/utils/format.js +32 -0
- package/dist/utils/format.js.map +1 -0
- package/dist/web-dashboard/attachment-extract.d.ts +64 -0
- package/dist/web-dashboard/attachment-extract.d.ts.map +1 -0
- package/dist/web-dashboard/attachment-extract.js +154 -0
- package/dist/web-dashboard/attachment-extract.js.map +1 -0
- package/dist/web-dashboard/chat-console.d.ts +108 -1
- package/dist/web-dashboard/chat-console.d.ts.map +1 -1
- package/dist/web-dashboard/chat-console.js +36 -0
- package/dist/web-dashboard/chat-console.js.map +1 -1
- package/dist/web-dashboard/hub-data.d.ts +37 -0
- package/dist/web-dashboard/hub-data.d.ts.map +1 -1
- package/dist/web-dashboard/hub-data.js +61 -1
- package/dist/web-dashboard/hub-data.js.map +1 -1
- package/dist/web-dashboard/process-env-inventory.d.ts +57 -0
- package/dist/web-dashboard/process-env-inventory.d.ts.map +1 -0
- package/dist/web-dashboard/process-env-inventory.js +96 -0
- package/dist/web-dashboard/process-env-inventory.js.map +1 -0
- package/dist/web-dashboard/server.d.ts +14 -0
- package/dist/web-dashboard/server.d.ts.map +1 -1
- package/dist/web-dashboard/server.js +493 -30
- package/dist/web-dashboard/server.js.map +1 -1
- package/dist/web-dashboard/src/types.d.ts +261 -48
- package/dist/web-dashboard/src/types.d.ts.map +1 -1
- package/package.json +18 -3
- package/src/web-dashboard/public/assets/{index-Cyd6tIew.css → index-CIJ6FHHZ.css} +1 -1
- package/src/web-dashboard/public/assets/index-CsySzR41.js +207 -0
- package/src/web-dashboard/public/assets/index-CsySzR41.js.map +1 -0
- package/src/web-dashboard/public/index.html +2 -2
- package/dist/tools/child-agent-worker.js +0 -212
- package/src/web-dashboard/public/assets/index-CxDj7p6i.js +0 -207
- package/src/web-dashboard/public/assets/index-CxDj7p6i.js.map +0 -1
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PDF text extraction — real parsing, table rows preserved.
|
|
3
|
+
*
|
|
4
|
+
* Why a parser is required (and why the previous implementation could never work):
|
|
5
|
+
* a PDF stores its glyphs inside FlateDecode-compressed content streams, positioned
|
|
6
|
+
* by a text matrix, with the glyph codes mapped back to characters through the
|
|
7
|
+
* font's encoding/ToUnicode CMap. Reading the file as UTF-8 and stripping
|
|
8
|
+
* non-printable bytes therefore recovers the *container*, never the text — which
|
|
9
|
+
* is what `read_extract` used to present as a successful extraction.
|
|
10
|
+
*
|
|
11
|
+
* `pdfjs-dist/legacy/build/pdf.mjs` is used rather than the modern build: pdf.js
|
|
12
|
+
* itself prints "Please use the `legacy` build in Node.js environments" for the
|
|
13
|
+
* default entry, and the legacy build is the one that avoids browser-only globals.
|
|
14
|
+
*
|
|
15
|
+
* Table structure is reconstructed from geometry, because a lab report is a table
|
|
16
|
+
* and the value/reference-range pairing is the whole point of reading it:
|
|
17
|
+
* - items are grouped into lines by their baseline `y` (transform[5]);
|
|
18
|
+
* - items on a line are ordered by `x` (transform[4]);
|
|
19
|
+
* - the horizontal gap between consecutive items decides the separator —
|
|
20
|
+
* nothing (a run split mid-word by kerning), a space, or ` | ` (a column
|
|
21
|
+
* boundary).
|
|
22
|
+
* A page with no text items at all is reported as having no text layer rather
|
|
23
|
+
* than returned as empty text, so a scanned report is a typed refusal instead of
|
|
24
|
+
* a silent blank.
|
|
25
|
+
*/
|
|
26
|
+
/** Thrown for inputs pdf.js parsed but could not read (encrypted, corrupt). */
|
|
27
|
+
export class PdfExtractError extends Error {
|
|
28
|
+
encrypted;
|
|
29
|
+
constructor(message, encrypted = false) {
|
|
30
|
+
super(message);
|
|
31
|
+
this.encrypted = encrypted;
|
|
32
|
+
this.name = 'PdfExtractError';
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/** Bump when the reconstruction rules change. */
|
|
36
|
+
export const PDF_EXTRACTOR_VERSION = 1;
|
|
37
|
+
/**
|
|
38
|
+
* Extract text from A4-ish PDF bytes, page by page.
|
|
39
|
+
*
|
|
40
|
+
* `maxChars` bounds the returned text; `truncated` says whether it was hit, so a
|
|
41
|
+
* caller can never mistake a partial document for the whole one.
|
|
42
|
+
*/
|
|
43
|
+
export async function extractPdfText(bytes, opts) {
|
|
44
|
+
const pdfjs = await import('pdfjs-dist/legacy/build/pdf.mjs');
|
|
45
|
+
// A document is untrusted input: no font/image fetching from inside the parser,
|
|
46
|
+
// and no font faces built for a canvas we never render to.
|
|
47
|
+
const task = pdfjs.getDocument({
|
|
48
|
+
data: bytes,
|
|
49
|
+
useWorkerFetch: false,
|
|
50
|
+
useSystemFonts: true,
|
|
51
|
+
disableFontFace: true,
|
|
52
|
+
});
|
|
53
|
+
let doc;
|
|
54
|
+
try {
|
|
55
|
+
doc = await task.promise;
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
const name = err?.name ?? '';
|
|
59
|
+
if (name === 'PasswordException') {
|
|
60
|
+
throw new PdfExtractError('The PDF is password-protected, so its text could not be read.', true);
|
|
61
|
+
}
|
|
62
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
63
|
+
throw new PdfExtractError(`The PDF could not be opened: ${msg}`);
|
|
64
|
+
}
|
|
65
|
+
const pages = [];
|
|
66
|
+
const pagesWithoutText = [];
|
|
67
|
+
let budget = opts.maxChars;
|
|
68
|
+
let truncated = false;
|
|
69
|
+
try {
|
|
70
|
+
for (let pageNo = 1; pageNo <= doc.numPages; pageNo++) {
|
|
71
|
+
const page = await doc.getPage(pageNo);
|
|
72
|
+
const content = await page.getTextContent();
|
|
73
|
+
const items = toPositionedItems(content.items);
|
|
74
|
+
const pageText = renderLines(items);
|
|
75
|
+
if (pageText.length === 0) {
|
|
76
|
+
pagesWithoutText.push(pageNo);
|
|
77
|
+
// Kept in the output: a missing page is information, and silently
|
|
78
|
+
// skipping it would misreport the document's shape.
|
|
79
|
+
pages.push(`[Page ${pageNo}: no extractable text — this page is an image (scanned) or empty]`);
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (pageText.length > budget) {
|
|
83
|
+
pages.push(pageText.slice(0, Math.max(0, budget)) + '\n…[page truncated]');
|
|
84
|
+
truncated = true;
|
|
85
|
+
budget = 0;
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
pages.push(pageText);
|
|
89
|
+
budget -= pageText.length;
|
|
90
|
+
}
|
|
91
|
+
page.cleanup();
|
|
92
|
+
if (budget <= 0 && pageNo < doc.numPages) {
|
|
93
|
+
truncated = true;
|
|
94
|
+
pages.push(`[Remaining ${doc.numPages - pageNo} page(s) not read — increase the character budget to read them]`);
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
finally {
|
|
100
|
+
// Release the parsed document (and its worker) — the loading task owns the
|
|
101
|
+
// lifecycle in pdf.js v5+, not the document proxy.
|
|
102
|
+
await task.destroy().catch(() => undefined);
|
|
103
|
+
}
|
|
104
|
+
const text = pages
|
|
105
|
+
.map((p, i) => (doc.numPages > 1 ? `[Page ${i + 1}]\n${p}` : p))
|
|
106
|
+
.join('\n\n');
|
|
107
|
+
return {
|
|
108
|
+
text,
|
|
109
|
+
pages: doc.numPages,
|
|
110
|
+
pagesWithText: doc.numPages - pagesWithoutText.length,
|
|
111
|
+
pagesWithoutText,
|
|
112
|
+
truncated,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
// ─── Internals ──────────────────────────────────────────────────────────────
|
|
116
|
+
/** Reduce pdf.js text items to positioned strings, dropping empty runs. */
|
|
117
|
+
function toPositionedItems(items) {
|
|
118
|
+
const out = [];
|
|
119
|
+
for (const raw of items) {
|
|
120
|
+
const it = raw;
|
|
121
|
+
const str = it.str ?? '';
|
|
122
|
+
if (str.trim().length === 0)
|
|
123
|
+
continue;
|
|
124
|
+
const t = it.transform;
|
|
125
|
+
if (!t || t.length < 6)
|
|
126
|
+
continue;
|
|
127
|
+
// transform = [a, b, c, d, e, f]; e/f are the run's x/y origin, d is the
|
|
128
|
+
// vertical scale — effectively the font size for horizontal text.
|
|
129
|
+
const size = Math.abs(t[3]) || Math.abs(t[0]) || 10;
|
|
130
|
+
out.push({
|
|
131
|
+
str,
|
|
132
|
+
x: t[4],
|
|
133
|
+
y: t[5],
|
|
134
|
+
width: it.width ?? 0,
|
|
135
|
+
size,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
return out;
|
|
139
|
+
}
|
|
140
|
+
/** Group items into visual lines and join each one. */
|
|
141
|
+
function renderLines(items) {
|
|
142
|
+
if (items.length === 0)
|
|
143
|
+
return '';
|
|
144
|
+
// Group by baseline. Tolerance scales with the glyph size so a line whose runs
|
|
145
|
+
// sit a hair apart vertically is not split into two rows.
|
|
146
|
+
const lines = [];
|
|
147
|
+
for (const item of items) {
|
|
148
|
+
const tol = Math.max(1.5, item.size * 0.4);
|
|
149
|
+
const line = lines.find((l) => Math.abs(l.y - item.y) <= tol);
|
|
150
|
+
if (line) {
|
|
151
|
+
line.items.push(item);
|
|
152
|
+
// Track the average so a slow drift across a wide table does not create
|
|
153
|
+
// a new line on every column.
|
|
154
|
+
line.y = (line.y * (line.items.length - 1) + item.y) / line.items.length;
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
lines.push({ y: item.y, items: [item] });
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
// PDF y grows upward, so the topmost line has the largest y.
|
|
161
|
+
lines.sort((a, b) => b.y - a.y);
|
|
162
|
+
return lines
|
|
163
|
+
.map((l) => joinLine(l.items.slice().sort((a, b) => a.x - b.x)))
|
|
164
|
+
.filter((l) => l.length > 0)
|
|
165
|
+
.join('\n');
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Join the runs of one line, choosing a separator from the gap between them.
|
|
169
|
+
* Gap thresholds are expressed in multiples of the glyph size (em).
|
|
170
|
+
*/
|
|
171
|
+
export function joinLine(items) {
|
|
172
|
+
let out = '';
|
|
173
|
+
let prev = null;
|
|
174
|
+
for (const item of items) {
|
|
175
|
+
const text = item.str.replace(/\s+$/, '');
|
|
176
|
+
if (text.length === 0)
|
|
177
|
+
continue;
|
|
178
|
+
if (prev === null) {
|
|
179
|
+
out = text;
|
|
180
|
+
prev = item;
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
const em = Math.max(prev.size, item.size) || 10;
|
|
184
|
+
const gap = item.x - (prev.x + prev.width);
|
|
185
|
+
let sep;
|
|
186
|
+
if (gap <= em * 0.12)
|
|
187
|
+
sep = ''; // same word, split by kerning
|
|
188
|
+
else if (gap <= em * 0.7)
|
|
189
|
+
sep = ' '; // a space within a phrase
|
|
190
|
+
else
|
|
191
|
+
sep = ' | '; // a column boundary
|
|
192
|
+
out += sep + text;
|
|
193
|
+
prev = item;
|
|
194
|
+
}
|
|
195
|
+
return out.replace(/\s+/g, ' ').trim();
|
|
196
|
+
}
|
|
197
|
+
//# sourceMappingURL=pdf.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pdf.js","sourceRoot":"","sources":["../../../src/tools/extract/pdf.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PPTX text extraction via `fflate` + direct XML reading.
|
|
3
|
+
*
|
|
4
|
+
* A `.pptx` is a ZIP of XML parts; slide text lives in `ppt/slides/slideN.xml`
|
|
5
|
+
* inside `<a:t>` runs, grouped into paragraphs by `<a:p>`. The reader unzips in
|
|
6
|
+
* memory, walks the slides in numeric order, and keeps each `<a:p>` on its own
|
|
7
|
+
* line — so bullet lines stay distinct rather than collapsing into one blob.
|
|
8
|
+
*
|
|
9
|
+
* `fflate` was chosen over a spreadsheet library's bundled ZIP code because it is
|
|
10
|
+
* a zero-dependency leaf and this is the only place a raw ZIP is needed. Spreadsheet
|
|
11
|
+
* text deliberately goes through SheetJS instead (see xlsx.ts) so cell formats are
|
|
12
|
+
* interpreted rather than guessed.
|
|
13
|
+
*/
|
|
14
|
+
export interface PptxExtractResult {
|
|
15
|
+
text: string;
|
|
16
|
+
slides: number;
|
|
17
|
+
truncated: boolean;
|
|
18
|
+
/** Slide numbers present in the file that carried no text at all. */
|
|
19
|
+
emptySlides: number[];
|
|
20
|
+
}
|
|
21
|
+
export declare class PptxExtractError extends Error {
|
|
22
|
+
constructor(message: string);
|
|
23
|
+
}
|
|
24
|
+
export declare function extractPptxText(bytes: Buffer, opts: {
|
|
25
|
+
maxChars: number;
|
|
26
|
+
}): Promise<PptxExtractResult>;
|
|
27
|
+
/**
|
|
28
|
+
* Pull the text out of one slide's XML: each `<a:p>` paragraph on its own line,
|
|
29
|
+
* its `<a:t>` runs concatenated (run splits are kerning, not word breaks).
|
|
30
|
+
*/
|
|
31
|
+
export declare function slideText(xml: string): string;
|
|
32
|
+
//# sourceMappingURL=pptx.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pptx.d.ts","sourceRoot":"","sources":["../../../src/tools/extract/pptx.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,OAAO,CAAC;IACnB,qEAAqE;IACrE,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,qBAAa,gBAAiB,SAAQ,KAAK;gBAC7B,OAAO,EAAE,MAAM;CAI5B;AAED,wBAAsB,eAAe,CACnC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,GACzB,OAAO,CAAC,iBAAiB,CAAC,CA8C5B;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAY7C"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PPTX text extraction via `fflate` + direct XML reading.
|
|
3
|
+
*
|
|
4
|
+
* A `.pptx` is a ZIP of XML parts; slide text lives in `ppt/slides/slideN.xml`
|
|
5
|
+
* inside `<a:t>` runs, grouped into paragraphs by `<a:p>`. The reader unzips in
|
|
6
|
+
* memory, walks the slides in numeric order, and keeps each `<a:p>` on its own
|
|
7
|
+
* line — so bullet lines stay distinct rather than collapsing into one blob.
|
|
8
|
+
*
|
|
9
|
+
* `fflate` was chosen over a spreadsheet library's bundled ZIP code because it is
|
|
10
|
+
* a zero-dependency leaf and this is the only place a raw ZIP is needed. Spreadsheet
|
|
11
|
+
* text deliberately goes through SheetJS instead (see xlsx.ts) so cell formats are
|
|
12
|
+
* interpreted rather than guessed.
|
|
13
|
+
*/
|
|
14
|
+
import { decodeEntities } from './html-text.js';
|
|
15
|
+
export class PptxExtractError extends Error {
|
|
16
|
+
constructor(message) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.name = 'PptxExtractError';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export async function extractPptxText(bytes, opts) {
|
|
22
|
+
const { unzipSync, strFromU8 } = await import('fflate');
|
|
23
|
+
let entries;
|
|
24
|
+
try {
|
|
25
|
+
entries = unzipSync(new Uint8Array(bytes));
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
29
|
+
throw new PptxExtractError(`The presentation could not be opened as a ZIP container: ${msg}`);
|
|
30
|
+
}
|
|
31
|
+
const slideNumbers = Object.keys(entries)
|
|
32
|
+
.map((path) => /^ppt\/slides\/slide(\d+)\.xml$/.exec(path))
|
|
33
|
+
.filter((m) => m !== null)
|
|
34
|
+
.map((m) => Number.parseInt(m[1], 10))
|
|
35
|
+
.sort((a, b) => a - b);
|
|
36
|
+
if (slideNumbers.length === 0) {
|
|
37
|
+
throw new PptxExtractError('No slides were found — the file does not look like a .pptx presentation.');
|
|
38
|
+
}
|
|
39
|
+
const parts = [];
|
|
40
|
+
const emptySlides = [];
|
|
41
|
+
let used = 0;
|
|
42
|
+
let truncated = false;
|
|
43
|
+
for (const n of slideNumbers) {
|
|
44
|
+
const xml = strFromU8(entries[`ppt/slides/slide${n}.xml`]);
|
|
45
|
+
const body = slideText(xml);
|
|
46
|
+
if (body.length === 0) {
|
|
47
|
+
emptySlides.push(n);
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
const chunk = `## Slide ${n}\n${body}`;
|
|
51
|
+
if (used + chunk.length > opts.maxChars) {
|
|
52
|
+
parts.push(`${chunk.slice(0, Math.max(0, opts.maxChars - used))}\n…[slides truncated]`);
|
|
53
|
+
truncated = true;
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
parts.push(chunk);
|
|
57
|
+
used += chunk.length;
|
|
58
|
+
}
|
|
59
|
+
return { text: parts.join('\n\n'), slides: slideNumbers.length, truncated, emptySlides };
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Pull the text out of one slide's XML: each `<a:p>` paragraph on its own line,
|
|
63
|
+
* its `<a:t>` runs concatenated (run splits are kerning, not word breaks).
|
|
64
|
+
*/
|
|
65
|
+
export function slideText(xml) {
|
|
66
|
+
const paragraphs = xml.split(/<\/a:p>/);
|
|
67
|
+
return paragraphs
|
|
68
|
+
.map((p) => {
|
|
69
|
+
const runs = [...p.matchAll(/<a:t(?:\s[^>]*)?>([\s\S]*?)<\/a:t>/g)]
|
|
70
|
+
.map((m) => decodeEntities(m[1]))
|
|
71
|
+
.join('');
|
|
72
|
+
return runs.replace(/\s+/g, ' ').trim();
|
|
73
|
+
})
|
|
74
|
+
.filter((line) => line.length > 0)
|
|
75
|
+
.join('\n');
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=pptx.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pptx.js","sourceRoot":"","sources":["../../../src/tools/extract/pptx.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XLSX text extraction via SheetJS (`@e965/xlsx`).
|
|
3
|
+
*
|
|
4
|
+
* A `.xlsx` is a ZIP of XML parts; the old implementation returned an empty string
|
|
5
|
+
* and an "requires xlsx library" error (honest, but useless).
|
|
6
|
+
*
|
|
7
|
+
* Why SheetJS and not a hand-rolled ZIP+XML reader: `raw: false` makes SheetJS
|
|
8
|
+
* apply each cell's number format, so a date cell comes out as a date rather than
|
|
9
|
+
* its serial (45000-ish). Emitting serials to a model reading a health panel would
|
|
10
|
+
* be exactly the silent-wrongness this workstream exists to remove, and re-deriving
|
|
11
|
+
* Excel's format rules is not a small job.
|
|
12
|
+
*
|
|
13
|
+
* Distribution note: the `xlsx` name on the public npm registry is stale at 0.18.5
|
|
14
|
+
* (SheetJS moved distribution to cdn.sheetjs.com). `@e965/xlsx` republishes the
|
|
15
|
+
* official 0.20.3 build on the registry with zero dependencies; it is pinned
|
|
16
|
+
* exactly in package.json. See TOOL_TRUTHFULNESS_TRACKER.md P1.1.
|
|
17
|
+
*
|
|
18
|
+
* Every cell is formatted to a string and rows become `a | b | c` lines, so the
|
|
19
|
+
* row/column pairing survives — the same shape as the CSV and DOCX readers.
|
|
20
|
+
*/
|
|
21
|
+
export interface XlsxExtractResult {
|
|
22
|
+
text: string;
|
|
23
|
+
sheets: string[];
|
|
24
|
+
/** Total non-empty rows across all sheets. */
|
|
25
|
+
rows: number;
|
|
26
|
+
truncated: boolean;
|
|
27
|
+
/** Sheet names skipped because the character budget ran out. */
|
|
28
|
+
skippedSheets: string[];
|
|
29
|
+
}
|
|
30
|
+
export declare class XlsxExtractError extends Error {
|
|
31
|
+
constructor(message: string);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Re-format every date cell to ISO, and drop its cached rendering (`w`) so it is
|
|
35
|
+
* recomputed.
|
|
36
|
+
*
|
|
37
|
+
* A sheet's own date format is usually `m/d/yy`, which is genuinely ambiguous —
|
|
38
|
+
* `3/4/26` is a different day either side of the Atlantic, and a model reasoning
|
|
39
|
+
* about a health trend across months cannot resolve it. `dateNF` alone does not win
|
|
40
|
+
* against a cell that already carries a format, which is why this is explicit.
|
|
41
|
+
* Exported for tests.
|
|
42
|
+
*/
|
|
43
|
+
export declare function normalizeDateFormats(sheet: unknown, iso: string): void;
|
|
44
|
+
export declare function extractXlsxText(bytes: Buffer, opts: {
|
|
45
|
+
maxChars: number;
|
|
46
|
+
}): Promise<XlsxExtractResult>;
|
|
47
|
+
//# sourceMappingURL=xlsx.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xlsx.d.ts","sourceRoot":"","sources":["../../../src/tools/extract/xlsx.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;IACnB,gEAAgE;IAChE,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,qBAAa,gBAAiB,SAAQ,KAAK;gBAC7B,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAUtE;AAYD,wBAAsB,eAAe,CACnC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,GACzB,OAAO,CAAC,iBAAiB,CAAC,CAsE5B"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XLSX text extraction via SheetJS (`@e965/xlsx`).
|
|
3
|
+
*
|
|
4
|
+
* A `.xlsx` is a ZIP of XML parts; the old implementation returned an empty string
|
|
5
|
+
* and an "requires xlsx library" error (honest, but useless).
|
|
6
|
+
*
|
|
7
|
+
* Why SheetJS and not a hand-rolled ZIP+XML reader: `raw: false` makes SheetJS
|
|
8
|
+
* apply each cell's number format, so a date cell comes out as a date rather than
|
|
9
|
+
* its serial (45000-ish). Emitting serials to a model reading a health panel would
|
|
10
|
+
* be exactly the silent-wrongness this workstream exists to remove, and re-deriving
|
|
11
|
+
* Excel's format rules is not a small job.
|
|
12
|
+
*
|
|
13
|
+
* Distribution note: the `xlsx` name on the public npm registry is stale at 0.18.5
|
|
14
|
+
* (SheetJS moved distribution to cdn.sheetjs.com). `@e965/xlsx` republishes the
|
|
15
|
+
* official 0.20.3 build on the registry with zero dependencies; it is pinned
|
|
16
|
+
* exactly in package.json. See TOOL_TRUTHFULNESS_TRACKER.md P1.1.
|
|
17
|
+
*
|
|
18
|
+
* Every cell is formatted to a string and rows become `a | b | c` lines, so the
|
|
19
|
+
* row/column pairing survives — the same shape as the CSV and DOCX readers.
|
|
20
|
+
*/
|
|
21
|
+
export class XlsxExtractError extends Error {
|
|
22
|
+
constructor(message) {
|
|
23
|
+
super(message);
|
|
24
|
+
this.name = 'XlsxExtractError';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Re-format every date cell to ISO, and drop its cached rendering (`w`) so it is
|
|
29
|
+
* recomputed.
|
|
30
|
+
*
|
|
31
|
+
* A sheet's own date format is usually `m/d/yy`, which is genuinely ambiguous —
|
|
32
|
+
* `3/4/26` is a different day either side of the Atlantic, and a model reasoning
|
|
33
|
+
* about a health trend across months cannot resolve it. `dateNF` alone does not win
|
|
34
|
+
* against a cell that already carries a format, which is why this is explicit.
|
|
35
|
+
* Exported for tests.
|
|
36
|
+
*/
|
|
37
|
+
export function normalizeDateFormats(sheet, iso) {
|
|
38
|
+
if (!sheet || typeof sheet !== 'object')
|
|
39
|
+
return;
|
|
40
|
+
for (const [key, cell] of Object.entries(sheet)) {
|
|
41
|
+
if (key.startsWith('!') || !cell || typeof cell !== 'object')
|
|
42
|
+
continue;
|
|
43
|
+
const c = cell;
|
|
44
|
+
if (c.t === 'd') {
|
|
45
|
+
c.z = iso;
|
|
46
|
+
delete c.w;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export async function extractXlsxText(bytes, opts) {
|
|
51
|
+
const mod = await import('@e965/xlsx');
|
|
52
|
+
const XLSX = (mod.default ?? mod);
|
|
53
|
+
if (typeof XLSX.read !== 'function' || typeof XLSX.utils?.sheet_to_json !== 'function') {
|
|
54
|
+
throw new XlsxExtractError('The XLSX reader (SheetJS) did not load correctly.');
|
|
55
|
+
}
|
|
56
|
+
let workbook;
|
|
57
|
+
try {
|
|
58
|
+
// `cellDates` surfaces genuine date cells as type 'd' so their presentation can
|
|
59
|
+
// be normalised below, instead of leaving them as numbers with a date format.
|
|
60
|
+
workbook = XLSX.read(bytes, { type: 'buffer', cellDates: true });
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
64
|
+
throw new XlsxExtractError(`The spreadsheet could not be read: ${msg}`);
|
|
65
|
+
}
|
|
66
|
+
const parts = [];
|
|
67
|
+
const sheets = [];
|
|
68
|
+
const skippedSheets = [];
|
|
69
|
+
let rows = 0;
|
|
70
|
+
let used = 0;
|
|
71
|
+
let truncated = false;
|
|
72
|
+
for (const name of workbook.SheetNames) {
|
|
73
|
+
if (used >= opts.maxChars) {
|
|
74
|
+
truncated = true;
|
|
75
|
+
skippedSheets.push(name);
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
normalizeDateFormats(workbook.Sheets[name], 'yyyy-mm-dd');
|
|
79
|
+
const table = XLSX.utils.sheet_to_json(workbook.Sheets[name], {
|
|
80
|
+
header: 1,
|
|
81
|
+
raw: false, // apply number formats: dates as dates, not serials
|
|
82
|
+
dateNF: 'yyyy-mm-dd',
|
|
83
|
+
defval: '',
|
|
84
|
+
blankrows: false,
|
|
85
|
+
});
|
|
86
|
+
const body = table
|
|
87
|
+
.map((row) => (Array.isArray(row) ? row.map((c) => String(c ?? '').trim()).join(' | ').trim() : ''))
|
|
88
|
+
.filter((line) => line.length > 0)
|
|
89
|
+
.join('\n');
|
|
90
|
+
if (body.length === 0) {
|
|
91
|
+
sheets.push(name);
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
const header = `## Sheet: ${name}`;
|
|
95
|
+
const chunk = `${header}\n${body}`;
|
|
96
|
+
const remaining = opts.maxChars - used;
|
|
97
|
+
if (chunk.length > remaining) {
|
|
98
|
+
parts.push(`${chunk.slice(0, Math.max(0, remaining))}\n…[sheet truncated]`);
|
|
99
|
+
truncated = true;
|
|
100
|
+
used = opts.maxChars;
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
parts.push(chunk);
|
|
104
|
+
used += chunk.length;
|
|
105
|
+
}
|
|
106
|
+
sheets.push(name);
|
|
107
|
+
rows += table.length;
|
|
108
|
+
}
|
|
109
|
+
return { text: parts.join('\n\n'), sheets, rows, truncated, skippedSheets };
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=xlsx.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xlsx.js","sourceRoot":"","sources":["../../../src/tools/extract/xlsx.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WS1 (#23) — `finding`: the model states a CLAIM, the gate decides the VERDICT.
|
|
3
|
+
*
|
|
4
|
+
* WHY A TOOL, AND WHY IT DOES NOT ACCEPT A VERDICT. `src/findings/verdicts.ts`
|
|
5
|
+
* is the vocabulary and the gate; it deliberately does not decide who may
|
|
6
|
+
* produce a finding (`intent-confirm` produces one for a routing decision). This
|
|
7
|
+
* is the producer for an ORDINARY turn on every surface, and the one design rule
|
|
8
|
+
* that matters is here: the model may supply the claim, the outcome and the
|
|
9
|
+
* evidence, and must NOT be able to supply the verdict. A tool argument called
|
|
10
|
+
* `verdict` would be the exact bypass the gate exists to refuse — the model
|
|
11
|
+
* typing "CONFIRMED" is the false-success defect with a nicer name — so the
|
|
12
|
+
* parameter does not exist. `confirmFinding` promotes to CONFIRMED only when at
|
|
13
|
+
* least one non-blank evidence reference was supplied, and refuses otherwise
|
|
14
|
+
* with the reason attached.
|
|
15
|
+
*
|
|
16
|
+
* WHAT THE MODEL SEES BACK is the rendered finding (`describeFinding`), including
|
|
17
|
+
* the honest line for a PLAUSIBLE one ("no evidence — reported as PLAUSIBLE, not
|
|
18
|
+
* verified"). That is the point of returning the render rather than a summary: a
|
|
19
|
+
* model that states a claim with no evidence is told, in the tool result it must
|
|
20
|
+
* act on, that the claim was recorded as unverified rather than as fact.
|
|
21
|
+
*
|
|
22
|
+
* THE STRUCTURED COPY travels to the surface on the context bus as
|
|
23
|
+
* `finding:recorded` (the same mechanism `plan_todo` uses for `plan:changed`),
|
|
24
|
+
* so every surface reports the SAME wire form — which is what
|
|
25
|
+
* `findings-verdicts@<surface>` is proved against.
|
|
26
|
+
*
|
|
27
|
+
* A CALL THAT RECORDED NOTHING IS A FAILURE. A blank claim or a blank outcome
|
|
28
|
+
* returns `Error:` (the loop's own accounting is `ok: !startsWith('Error:')`),
|
|
29
|
+
* because a finding with no claim is not a finding — and reporting one as a
|
|
30
|
+
* successful call would be this repository's recurring defect in miniature.
|
|
31
|
+
*/
|
|
32
|
+
import { z } from 'zod';
|
|
33
|
+
import { type Finding } from '../findings/verdicts.js';
|
|
34
|
+
import type { Tool, ToolContext } from './registry.js';
|
|
35
|
+
/** The registry name — one place, so the toolset/core lists and the tests agree. */
|
|
36
|
+
export declare const FINDING_TOOL_NAME = "finding";
|
|
37
|
+
/**
|
|
38
|
+
* The bus event carrying the structured wire finding.
|
|
39
|
+
*
|
|
40
|
+
* Named `finding:recorded` (past tense) on purpose: the event is emitted AFTER
|
|
41
|
+
* the gate has decided, so a listener never sees a claim whose verdict is still
|
|
42
|
+
* being computed — the same convention `plan:changed` follows.
|
|
43
|
+
*/
|
|
44
|
+
export declare const FINDING_EVENT = "finding:recorded";
|
|
45
|
+
/** `finding` args. There is deliberately no `verdict` field — see the header. */
|
|
46
|
+
export declare const findingSchema: z.ZodObject<{
|
|
47
|
+
claim: z.ZodString;
|
|
48
|
+
outcome: z.ZodString;
|
|
49
|
+
evidence: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
50
|
+
kind: z.ZodEnum<{
|
|
51
|
+
command: "command";
|
|
52
|
+
quote: "quote";
|
|
53
|
+
file: "file";
|
|
54
|
+
observation: "observation";
|
|
55
|
+
}>;
|
|
56
|
+
ref: z.ZodString;
|
|
57
|
+
detail: z.ZodOptional<z.ZodString>;
|
|
58
|
+
}, z.core.$strip>>>;
|
|
59
|
+
}, z.core.$strip>;
|
|
60
|
+
export type FindingToolArgs = z.infer<typeof findingSchema>;
|
|
61
|
+
/**
|
|
62
|
+
* Record one finding: build it PLAUSIBLE, let the gate decide, report it.
|
|
63
|
+
*
|
|
64
|
+
* Split out from the `Tool` wrapper so the behaviour is testable without the
|
|
65
|
+
* registry, and so a caller that already has a context (the child process) can
|
|
66
|
+
* reuse it.
|
|
67
|
+
*/
|
|
68
|
+
export declare function recordFinding(args: FindingToolArgs, ctx?: Pick<ToolContext, 'emit'>): {
|
|
69
|
+
finding: Finding;
|
|
70
|
+
text: string;
|
|
71
|
+
} | {
|
|
72
|
+
error: string;
|
|
73
|
+
};
|
|
74
|
+
/** The `finding` tool as the registry takes it. */
|
|
75
|
+
export declare function createFindingTool(): Tool;
|
|
76
|
+
//# sourceMappingURL=finding-tool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-tool.d.ts","sourceRoot":"","sources":["../../src/tools/finding-tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAML,KAAK,OAAO,EACb,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEvD,oFAAoF;AACpF,eAAO,MAAM,iBAAiB,YAAY,CAAC;AAE3C;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,qBAAqB,CAAC;AAKhD,iFAAiF;AACjF,eAAO,MAAM,aAAa;;;;;;;;;;;;;iBA2BxB,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAE5D;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,eAAe,EACrB,GAAG,CAAC,EAAE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,GAC9B;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CA2CxD;AAED,mDAAmD;AACnD,wBAAgB,iBAAiB,IAAI,IAAI,CAexC"}
|