@arnilo/prism 0.3.2 → 0.4.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 (116) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +34 -57
  3. package/dist/agent-run-lifecycle.js +4 -0
  4. package/dist/agent-run-state.d.ts +4 -0
  5. package/dist/agent-run-state.js +18 -5
  6. package/dist/agent-session/session.d.ts +7 -0
  7. package/dist/agent-session/session.js +59 -2
  8. package/dist/cli-dev.d.ts +29 -0
  9. package/dist/cli-dev.js +52 -0
  10. package/dist/cli-init.d.ts +17 -2
  11. package/dist/cli-init.js +194 -21
  12. package/dist/cli-runner.d.ts +5 -1
  13. package/dist/cli-runner.js +12 -1
  14. package/dist/contracts-core/agent.d.ts +6 -0
  15. package/dist/contracts-protocol.d.ts +18 -0
  16. package/dist/contracts-run-state.d.ts +1 -2
  17. package/dist/index.d.ts +3 -1
  18. package/dist/index.js +2 -1
  19. package/dist/input.d.ts +8 -0
  20. package/dist/input.js +4 -0
  21. package/dist/testing/persistence-schema.d.ts +1 -1
  22. package/dist/testing/persistence-schema.js +32 -28
  23. package/dist/testing/tool-conformance.d.ts +25 -0
  24. package/dist/testing/tool-conformance.js +128 -1
  25. package/dist/tool-search.d.ts +76 -0
  26. package/dist/tool-search.js +199 -0
  27. package/docs/0.1.0-readiness.md +2 -2
  28. package/docs/acp-agent.md +1 -1
  29. package/docs/antigravity-agent.md +1 -1
  30. package/docs/browser-automation.md +5 -5
  31. package/docs/caveman.md +2 -2
  32. package/docs/cli-rpc.md +26 -3
  33. package/docs/coding-security.md +1 -1
  34. package/docs/coding-tools.md +82 -0
  35. package/docs/compaction-and-retry.md +2 -2
  36. package/docs/compaction-llm.md +4 -4
  37. package/docs/compaction-observational-memory.md +3 -3
  38. package/docs/context-and-skills.md +2 -0
  39. package/docs/core.md +85 -0
  40. package/docs/credential-storage.md +1 -1
  41. package/docs/database-persistence.md +4 -0
  42. package/docs/dev-inspector.md +103 -0
  43. package/docs/diagrams.md +247 -0
  44. package/docs/documents.md +213 -0
  45. package/docs/evaluations.md +35 -1
  46. package/docs/graft.md +3 -3
  47. package/docs/guardrails.md +1 -1
  48. package/docs/host-security.md +4 -3
  49. package/docs/impeccable.md +2 -2
  50. package/docs/index.md +31 -20
  51. package/docs/mcp-tools.md +1 -1
  52. package/docs/migrate-to-0.4.md +312 -0
  53. package/docs/migration.md +22 -0
  54. package/docs/model-routing.md +1 -1
  55. package/docs/multi-agent-patterns.md +177 -0
  56. package/docs/multimodal-content.md +1 -1
  57. package/docs/obscura.md +10 -10
  58. package/docs/openapi-tools.md +1 -1
  59. package/docs/performance.md +23 -3
  60. package/docs/persistence-credentials-multimodality-primitives.md +1 -1
  61. package/docs/policy-and-audit.md +1 -1
  62. package/docs/ponytail.md +2 -2
  63. package/docs/prompt-registry.md +106 -0
  64. package/docs/provider-caching.md +32 -32
  65. package/docs/provider-conformance.md +1 -1
  66. package/docs/provider-packages.md +19 -19
  67. package/docs/provider-primitives.md +4 -4
  68. package/docs/providers/ai-sdk.md +3 -3
  69. package/docs/providers/alibaba.md +5 -5
  70. package/docs/providers/anthropic.md +6 -6
  71. package/docs/providers/azure.md +3 -3
  72. package/docs/providers/bedrock.md +3 -3
  73. package/docs/providers/clinepass.md +3 -3
  74. package/docs/providers/deepseek.md +3 -3
  75. package/docs/providers/google.md +4 -4
  76. package/docs/providers/kimi.md +3 -3
  77. package/docs/providers/neuralwatt.md +8 -8
  78. package/docs/providers/ollama.md +3 -3
  79. package/docs/providers/openai-compatible.md +1 -1
  80. package/docs/providers/openai.md +5 -5
  81. package/docs/providers/opencode-go.md +4 -4
  82. package/docs/providers/openrouter.md +3 -3
  83. package/docs/providers/vertex.md +5 -5
  84. package/docs/providers/xai.md +3 -3
  85. package/docs/providers/zai.md +3 -3
  86. package/docs/rag.md +5 -5
  87. package/docs/release-and-install.md +98 -50
  88. package/docs/runs-and-usage.md +14 -1
  89. package/docs/server.md +90 -1
  90. package/docs/sheets.md +229 -0
  91. package/docs/supervisors.md +1 -0
  92. package/docs/thinking-and-reasoning.md +10 -10
  93. package/docs/tool-conformance.md +27 -2
  94. package/docs/tools.md +29 -2
  95. package/docs/web-tools.md +2 -2
  96. package/docs/wiki.md +6 -6
  97. package/docs/workflow-orchestration-primitives.md +24 -0
  98. package/docs/workflows.md +70 -9
  99. package/docs/working-and-semantic-memory.md +53 -5
  100. package/package.json +10 -30
  101. package/templates/README.md +23 -0
  102. package/templates/deep-research/README.md.tmpl +47 -0
  103. package/templates/deep-research/env.example.tmpl +12 -0
  104. package/templates/deep-research/gitignore.tmpl +7 -0
  105. package/templates/deep-research/manifest.json +12 -0
  106. package/templates/deep-research/package.json.tmpl +23 -0
  107. package/templates/deep-research/src/agent.ts.tmpl +81 -0
  108. package/templates/deep-research/src/index.ts.tmpl +53 -0
  109. package/templates/deep-research/src/tests/research.test.ts.tmpl +114 -0
  110. package/templates/deep-research/src/tools.ts.tmpl +86 -0
  111. package/templates/deep-research/src/types.ts.tmpl +45 -0
  112. package/templates/deep-research/src/workflow.ts.tmpl +156 -0
  113. package/templates/deep-research/tsconfig.json.tmpl +15 -0
  114. package/templates/init/manifest.json +5 -0
  115. package/templates/init/package.json.tmpl +2 -1
  116. package/templates/init/providers.json +16 -16
package/docs/sheets.md ADDED
@@ -0,0 +1,229 @@
1
+ # Spreadsheets, CSV parsing, and typed schema inference (`@arnilo/prism-office/sheets`)
2
+
3
+ ## What it does
4
+
5
+ The `@arnilo/prism-office/sheets` package provides fail-closed, high-fidelity spreadsheet (XLSX) and delimiter-separated (CSV/TSV/PSV) data ingestion with automatic dialect sniffing, typed column schema inference, and **strict financial decimal safety**.
6
+
7
+ ### Headline Guarantee: Strict Financial Decimal Safety
8
+
9
+ > [!IMPORTANT]
10
+ > **Zero Float Coercion on Decimal Paths**:
11
+ > In financial and enterprise data processing, floating-point rounding errors (IEEE-754 `double`) silently distort monetary totals, balance ledgers, and transaction reconciliations.
12
+ >
13
+ > In `@arnilo/prism-office/sheets`:
14
+ > - Money-like and decimal values are **never converted to JavaScript numbers (`Number()`, `parseFloat()`, or unary `+`)**.
15
+ > - All decimal and currency values are parsed, normalized, and emitted as exact canonical decimal strings: `{ type: "decimal", value: "1234.56" }`.
16
+ > - Currency markers (`$`, `€`, `£`, `¥`, `₹`, `CHF`, `USD`, `EUR`, etc.) and accounting parentheses `($1,234.56)` are normalized safely into canonical strings (`"-1234.56"`).
17
+ > - Ambiguous numbers (e.g. scientific notation `1.23e5` or inconsistent locale separators) are preserved as raw strings with `flags: ["numeric-ambiguous"]` rather than guessed.
18
+ > - An automated source-scan test in the regression suite enforces that no floating-point conversions exist on decimal paths across the codebase.
19
+
20
+ ### Core Capabilities
21
+
22
+ - **Pure In-Memory Operation**: Accepts `Uint8Array` binary archives or `string | Uint8Array` CSV text. Zero filesystem access, zero network I/O, zero `process.env` lookups, and zero background worker threads.
23
+ - **Fail-Closed Container Gating**: XLSX archives must begin with the standard ZIP container magic signature (`PK\x03\x04`). CSV inputs support UTF-8 (with automatic BOM stripping); UTF-16 encoded buffers are refused fail-closed with `ERR_PRISM_SHEETS_UNSUPPORTED_FORMAT`.
24
+ - **Automatic CSV Dialect Sniffing**: Analyzes the first 4 KiB / 50 lines to detect candidate delimiters (`,`, `;`, `\t`, `|`) using variance-based scoring outside quoted regions, correctly distinguishing delimiters from commas within quoted addresses or company names.
25
+ - **RFC 4180 State Machine**: Handles embedded newlines in quoted fields, doubled-quote escaping (`""`), and CRLF / LF line endings in a single, non-backtracking linear pass (<60 ms for 1 MB CSVs).
26
+ - **Read-Only Formula Preservation**: OpenXML formula cells are extracted as `{ type: "formula", formula: "=SUM(A1:A10)", cachedValue: "100" }` and are **never evaluated or executed**.
27
+ - **Bounded Sampling Window + Full Validation**: Infers column types across an initial window (default 500 rows) and validates all subsequent records against the inferred schema, reporting structured `type-mismatch` warnings without silent coercion.
28
+ - **Privacy-Guaranteed Telemetry Seam**: Optional, dependency-free `SheetsTelemetry` hook emits `sheets.parse` spans carrying byte, row, column, and duration metrics with zero cell text or confidential payload leakage.
29
+
30
+ ## When to use it
31
+
32
+ Use `@arnilo/prism-office/sheets` when autonomous agents, data pipelines, or enterprise workflows need to:
33
+ 1. Ingest untrusted customer XLSX or CSV files with strict, unbypassable byte, row, column, and sheet caps.
34
+ 2. Parse tabular financial records, invoices, ledgers, or pricing sheets with mathematical decimal precision guarantees.
35
+ 3. Automatically determine CSV delimiters, quotes, and headers without manual dialect configuration.
36
+ 4. Extract structural column schemas (`name`, `type`, `nullRate`, `sample`, `flags`) for automated data cataloging, SQL generation, or tool routing.
37
+
38
+ Do **not** use this package for interactive spreadsheet calculation/formula engines, macro execution, chart generation, or real-time collaborative editing.
39
+
40
+ ## Inputs / request
41
+
42
+ ### Primary Parsing Functions
43
+
44
+ | Function | Signature | Description |
45
+ | --- | --- | --- |
46
+ | `parseWorkbook` | `(bytes: Uint8Array, options?: ParseWorkbookOptions) => Promise<WorkbookParse>` | Parses an XLSX workbook binary buffer, enforces caps and ZIP signature gating, extracts raw cells, resolves shared strings and styles, and infers schemas per worksheet. |
47
+ | `parseCsv` | `(input: string \| Uint8Array, options?: ParseCsvOptions) => Promise<CsvParse>` | Parses CSV/TSV/PSV input with automatic dialect sniffing, BOM stripping, RFC 4180 state machine processing, and schema inference. |
48
+ | `inferAndTransformRows` | `(rows: readonly (readonly CellValue[])[], caps: ResolvedSheetsCaps) => InferAndTransformResult` | Pure function that infers column types over a sampling window and validates full rows against the inferred schema. |
49
+
50
+ ### Decimal & Normalization Utilities
51
+
52
+ | Function | Signature | Description |
53
+ | --- | --- | --- |
54
+ | `normalizeDecimal` | `(input: string) => NormalizedDecimalResult \| null` | Normalizes currency strings, accounting negatives, and thousands separators into canonical decimal strings (`^-?\d+(\.\d+)?$`). Returns `null` if ambiguous or invalid. |
55
+ | `isCanonicalDecimal` | `(str: string) => boolean` | Checks if a string is already in canonical decimal format (e.g. `"1234.56"`, `"-0.05"`, `"42"`). |
56
+ | `isCurrencyString` | `(str: string) => boolean` | Checks if a string contains explicit currency symbols or currency codes (`$`, `€`, `USD`, etc.). |
57
+ | `isScientificNotation` | `(str: string) => boolean` | Checks if a string represents scientific notation (e.g. `"1.23e5"`, `"4.56E-3"`). |
58
+
59
+ ### Cap Validation Utilities
60
+
61
+ | Function | Signature | Description |
62
+ | --- | --- | --- |
63
+ | `resolveSheetsCaps` | `(caps?: SheetsCaps) => ResolvedSheetsCaps` | Resolves user-configured limits against safe defaults and hard ceilings. |
64
+ | `validateByteCap` | `(byteLength: number, caps: ResolvedSheetsCaps) => void` | Validates input size against `caps.maxBytes`, throwing `SheetsCapError` if exceeded. |
65
+ | `validateZipSignature` | `(bytes: Uint8Array) => void` | Validates standard PKZIP magic bytes (`0x50, 0x4b, 0x03, 0x04`), throwing `SheetsFormatError` if missing. |
66
+
67
+ ### Capacity Limits and Defaults
68
+
69
+ Limits are verified upfront and enforced progressively to prevent memory exhaustion and denial-of-service from adversarial inputs:
70
+
71
+ | Cap | Default | Hard Ceiling | Description |
72
+ | --- | --- | --- | --- |
73
+ | `maxBytes` | 32 MiB (`33,554,432`) | 512 MiB (`536,870,912`) | Maximum input buffer or string byte length. |
74
+ | `maxSheets` | 100 | 1,000 | Maximum worksheets in a workbook archive. |
75
+ | `maxRows` | 100,000 | 1,000,000 | Maximum rows per worksheet or CSV document. |
76
+ | `maxColumns` | 1,000 | 16,384 | Maximum columns per worksheet or CSV document. |
77
+ | `inferenceWindowRows` | 500 | 5,000 | Number of rows sampled for column schema inference. |
78
+ | `maxWarnings` | 100 | 1,000 | Maximum validation and dialect warnings recorded. |
79
+
80
+ ## Outputs / response / events
81
+
82
+ ### Error Hierarchy
83
+
84
+ All error classes inherit from `SheetsError` and carry structured error codes:
85
+
86
+ | Error Class | Code | Cause / Trigger |
87
+ | --- | --- | --- |
88
+ | `SheetsCapError` | `ERR_PRISM_SHEETS_CAP` | Input size, sheet count, row count, or column count exceeds configured caps. |
89
+ | `SheetsValidationError` | `ERR_PRISM_SHEETS_VALIDATION` | Invalid cap configuration (e.g. non-integer or exceeding hard ceiling). |
90
+ | `SheetsFormatError` | `ERR_PRISM_SHEETS_UNSUPPORTED_FORMAT` | Missing ZIP container signature on XLSX input or unsupported encoding (UTF-16) on CSV input. |
91
+ | `SheetsParseError` | `ERR_PRISM_SHEETS_PARSE_FAILED` | Corrupt archive structure or malformed XML parts. |
92
+
93
+ ### Schema & Warnings Output Shape
94
+
95
+ ```ts
96
+ export interface ColumnSchema {
97
+ readonly name: string;
98
+ readonly type: "string" | "integer" | "number" | "decimal" | "date" | "datetime" | "boolean";
99
+ readonly nullRate: number;
100
+ readonly sample?: string;
101
+ readonly flags?: readonly ("numeric-ambiguous" | "mixed-types")[];
102
+ }
103
+
104
+ export interface InferenceWarning {
105
+ readonly row: number;
106
+ readonly column: number;
107
+ readonly columnName?: string;
108
+ readonly expectedType: string;
109
+ readonly actualValue: string;
110
+ readonly message: string;
111
+ }
112
+ ```
113
+
114
+ ## Request/response example
115
+
116
+ ### Input CSV
117
+
118
+ ```csv
119
+ transaction_id,item_description,total_amount,unit_price,refund_fee
120
+ TXN-1001,"Software Consulting","$1,234.56",1234.56,"($ 50.00)"
121
+ TXN-1002,"Cloud Server Hosting","€ 450.00",450.00,"-$ 10.00"
122
+ TXN-1003,"Hardware Device","£ 2,500.00",2500.00,"$ 0.00"
123
+ ```
124
+
125
+ ### Parsed Output (`CsvParse`)
126
+
127
+ ```json
128
+ {
129
+ "dialect": {
130
+ "delimiter": ",",
131
+ "quote": "\"",
132
+ "hasHeader": true
133
+ },
134
+ "schema": [
135
+ { "name": "transaction_id", "type": "string", "nullRate": 0, "sample": "TXN-1001", "flags": [] },
136
+ { "name": "item_description", "type": "string", "nullRate": 0, "sample": "Software Consulting", "flags": [] },
137
+ { "name": "total_amount", "type": "decimal", "nullRate": 0, "sample": "$1,234.56", "flags": [] },
138
+ { "name": "unit_price", "type": "decimal", "nullRate": 0, "sample": "1234.56", "flags": [] },
139
+ { "name": "refund_fee", "type": "decimal", "nullRate": 0, "sample": "($ 50.00)", "flags": [] }
140
+ ],
141
+ "rows": [
142
+ ["transaction_id", "item_description", "total_amount", "unit_price", "refund_fee"],
143
+ ["TXN-1001", "Software Consulting", { "type": "decimal", "value": "1234.56" }, { "type": "decimal", "value": "1234.56" }, { "type": "decimal", "value": "-50.00" }],
144
+ ["TXN-1002", "Cloud Server Hosting", { "type": "decimal", "value": "450.00" }, { "type": "decimal", "value": "450.00" }, { "type": "decimal", "value": "-10.00" }],
145
+ ["TXN-1003", "Hardware Device", { "type": "decimal", "value": "2500.00" }, { "type": "decimal", "value": "2500.00" }, { "type": "decimal", "value": "0.00" }]
146
+ ],
147
+ "warnings": []
148
+ }
149
+ ```
150
+
151
+ ## Implementation example
152
+
153
+ ```ts
154
+ import { parseWorkbook, parseCsv, type SheetsTelemetry } from "@arnilo/prism-office/sheets";
155
+
156
+ // 1. Parse XLSX workbook with custom caps
157
+ const xlsxBytes = new Uint8Array([...]); // Untrusted file bytes
158
+ const workbook = await parseWorkbook(xlsxBytes, {
159
+ caps: {
160
+ maxRows: 50_000,
161
+ maxColumns: 500,
162
+ maxBytes: 16 * 1024 * 1024,
163
+ },
164
+ });
165
+
166
+ for (const sheet of workbook.sheets) {
167
+ console.log(`Sheet: ${sheet.name} (${sheet.rows.length} rows)`);
168
+ for (const col of sheet.schema) {
169
+ console.log(` Column [${col.name}] inferred as ${col.type} (nullRate: ${col.nullRate})`);
170
+ }
171
+ }
172
+
173
+ // 2. Parse CSV with automatic dialect sniffing and telemetry
174
+ const csvText = `id;name;revenue\n1;"Acme, Corp";1000000.00\n2;"Global, Inc";500000.50\n`;
175
+
176
+ const telemetry: SheetsTelemetry = {
177
+ startSpan(name, attributes) {
178
+ console.log(`Span started: ${name}`, attributes);
179
+ return {
180
+ setAttribute() {},
181
+ addEvent() {},
182
+ recordError() {},
183
+ end() {},
184
+ };
185
+ },
186
+ };
187
+
188
+ const csvResult = await parseCsv(csvText, { telemetry });
189
+ console.log(`Detected delimiter: "${csvResult.dialect.delimiter}"`);
190
+ console.log(`Revenue value:`, csvResult.rows[1][2]);
191
+ // Output: { type: "decimal", value: "1000000.00" }
192
+ ```
193
+
194
+ ## Extension and configuration notes
195
+
196
+ ### Sub-package Pinning
197
+ To avoid pulling in CLI frameworks or extraneous dependencies, `@arnilo/prism-office/sheets` directly pins the exact underlying modular packages:
198
+ - `@office-open/xlsx@0.12.3`
199
+ - `@office-open/xml@0.12.3`
200
+
201
+ ### Custom Telemetry Hook
202
+ The `SheetsTelemetry` seam allows optional OpenTelemetry instrumentation without adding runtime telemetry dependencies:
203
+ ```ts
204
+ const telemetry: SheetsTelemetry = {
205
+ startSpan(name, attributes) {
206
+ // Maps to tracer.startSpan with allow-listed metadata (bytes, rows, columns, sheetCount)
207
+ // Cell contents and user data are NEVER passed to telemetry spans.
208
+ return activeSpan;
209
+ },
210
+ };
211
+ ```
212
+
213
+ ### Self-Hosting & Operational Notes
214
+ - **Zero Network & Storage Dependencies**: `@arnilo/prism-office/sheets` does not write files or contact network services. Host engines own persistence, storage buckets, and lake datasets.
215
+ - **Fail-Closed Container Gating**: Malicious or non-standard files are rejected before allocation or XML decompression occurs.
216
+
217
+ ## Security and performance notes
218
+
219
+ - **Pure In-Memory Operation**: No temporary files, no shell execution, no binary spawning, and zero network sockets.
220
+ - **ZIP Signature Gating**: Buffers must begin with standard PKZIP container signatures (`0x50, 0x4B, 0x03, 0x04`). Extension-based type inference is strictly prohibited.
221
+ - **Fail-Closed Caps**: Input size and element count caps are evaluated before entering XML translation passes, preventing zip-bomb and decompression amplification attacks.
222
+ - **Strict Anti-Corruption Invariant**: Zero floating-point conversions on decimal paths guarantee exact financial calculations and ledger balances.
223
+ - **Performance Budget**: Warm parsing of 1 MB CSV files completes in under 60 ms; workbook parsing completes in under 100 ms.
224
+
225
+ ## Related APIs
226
+
227
+ - [`@arnilo/prism-office/documents`](./documents.md): Specification-compliant OpenXML document generation, parsing, patching, and preview rendering for DOCX, XLSX, and PPTX.
228
+ - [`@arnilo/prism-document-reader`](./document-reader.md): Bounded literal text extraction from PDF and DOCX documents for coding agent tools.
229
+ - [`@arnilo/prism-observability-opentelemetry`](./observability.md): OpenTelemetry instrumentation and trace adapters.
@@ -78,6 +78,7 @@ Supervisors propagate parent `identity` and `effectStore` to every child agent/r
78
78
 
79
79
  ## Related APIs
80
80
 
81
+ - [Multi-agent patterns](multi-agent-patterns.md): the decision table comparing this delegation pattern with in-session handoff (swarm) and A2A; handoff keeps one transcript, delegation keeps separate child runs — choose by boundary, budget, and telemetry needs.
81
82
  - [Agent identity](agent-identity.md): host-verified identity and narrow delegation.
82
83
  - [A2A interoperability](a2a.md): separate remote protocol boundary. `A2ATaskLifecycle` adapts host durable agent/workflow state directly; it does not route A2A execution through local supervisor child planning.
83
84
  - [Workflows](workflows.md): preferred deterministic orchestration.
@@ -59,16 +59,16 @@ Core maps only shapes shared by ≥2 packages (or an explicit no-op). Unique kno
59
59
 
60
60
  | Package | Recommended family | Notes |
61
61
  | --- | --- | --- |
62
- | `@arnilo/prism-provider-openai` | `openai_reasoning` | First-class body `reasoning` from model + per-turn compat merge; `summary`/`mode`/`context` via compat |
63
- | `@arnilo/prism-provider-openrouter` | `openai_reasoning` | First-class `resolveOpenRouterReasoning` merge; prefer `reasoning` object over legacy `reasoning_effort` shorthand; `preserveThinking` replays as body `reasoning` |
64
- | `@arnilo/prism-provider-zai` | `reasoning_effort` (+ optional `thinking_type`) | Official `thinking` / `reasoning_effort` / `tool_stream` / `clear_thinking`; Preserved Thinking via `reasoning_content` |
65
- | `@arnilo/prism-provider-neuralwatt` | `reasoning_effort` | Budgets / `preserve_thinking` / `clear_thinking` / `chat_template_kwargs` stay package-local on `compat` |
66
- | `@arnilo/prism-provider-kimi` | K3: `reasoning_effort`; K2.x: `thinking_type` | K2.7-code thinking is always on; do not send conflicting `thinking` + `reasoning_effort` |
67
- | `@arnilo/prism-provider-opencode-go` | Anthropic route: thinking blocks (`thinking_type` family); OpenAI route: `reasoning_content` preserve + optional `thinking`/`reasoning_effort`/`reasoning` passthrough | Official dual endpoints; MiniMax/Qwen → Anthropic, others → OpenAI |
68
- | `@arnilo/prism-provider-ai-sdk` | `noop` | Host `LanguageModelV4` owns reasoning settings |
69
- | `@arnilo/prism-provider-deepseek` | `thinking_type` + `reasoning_effort` | Thinking on by default (`high`). `cacheRetention: "none"` or `thinking: false` disables. Tool turns must replay `reasoning_content` or the API returns 400. |
70
- | `@arnilo/prism-provider-xai` | replay only | Featured Completions do not send `reasoning_effort`. Reasoning models must replay `reasoning_content` or the prefix cache breaks. Do not flatten thinking into text. |
71
- | `@arnilo/prism-provider-clinepass` | `reasoning_effort` | Per-model `compat.thinkingLevelMap`. GLM `xhigh` passthrough (never send `max`). K3 `high` → `max`. Unsupported slots omit the field. |
62
+ | `@arnilo/prism-providers/openai` | `openai_reasoning` | First-class body `reasoning` from model + per-turn compat merge; `summary`/`mode`/`context` via compat |
63
+ | `@arnilo/prism-providers/openrouter` | `openai_reasoning` | First-class `resolveOpenRouterReasoning` merge; prefer `reasoning` object over legacy `reasoning_effort` shorthand; `preserveThinking` replays as body `reasoning` |
64
+ | `@arnilo/prism-providers/zai` | `reasoning_effort` (+ optional `thinking_type`) | Official `thinking` / `reasoning_effort` / `tool_stream` / `clear_thinking`; Preserved Thinking via `reasoning_content` |
65
+ | `@arnilo/prism-providers/neuralwatt` | `reasoning_effort` | Budgets / `preserve_thinking` / `clear_thinking` / `chat_template_kwargs` stay package-local on `compat` |
66
+ | `@arnilo/prism-providers/kimi` | K3: `reasoning_effort`; K2.x: `thinking_type` | K2.7-code thinking is always on; do not send conflicting `thinking` + `reasoning_effort` |
67
+ | `@arnilo/prism-providers/opencode-go` | Anthropic route: thinking blocks (`thinking_type` family); OpenAI route: `reasoning_content` preserve + optional `thinking`/`reasoning_effort`/`reasoning` passthrough | Official dual endpoints; MiniMax/Qwen → Anthropic, others → OpenAI |
68
+ | `@arnilo/prism-providers/ai-sdk` | `noop` | Host `LanguageModelV4` owns reasoning settings |
69
+ | `@arnilo/prism-providers/deepseek` | `thinking_type` + `reasoning_effort` | Thinking on by default (`high`). `cacheRetention: "none"` or `thinking: false` disables. Tool turns must replay `reasoning_content` or the API returns 400. |
70
+ | `@arnilo/prism-providers/xai` | replay only | Featured Completions do not send `reasoning_effort`. Reasoning models must replay `reasoning_content` or the prefix cache breaks. Do not flatten thinking into text. |
71
+ | `@arnilo/prism-providers/clinepass` | `reasoning_effort` | Per-model `compat.thinkingLevelMap`. GLM `xhigh` passthrough (never send `max`). K3 `high` → `max`. Unsupported slots omit the field. |
72
72
 
73
73
  `thinkingFamilyForModel` infers family from existing `compat` shape, then safe provider heuristics (`openai*` → `openai_reasoning`, `neuralwatt` → `reasoning_effort`), then `capabilities.reasoning` → `reasoning_effort`, else `noop`. Docs and packages may map other provider ids explicitly; core avoids provider-specific literals beyond those heuristics.
74
74
 
@@ -2,14 +2,15 @@
2
2
 
3
3
  ## What it does
4
4
 
5
- Tool conformance helpers are dependency-free assertions for tool-dispatch configuration tests. They exercise the blocked-reason matrix and the success path of `dispatchToolCall` without network or credentials.
5
+ Tool conformance helpers are dependency-free assertions for tool-dispatch configuration tests. They exercise the blocked-reason matrix and the success path of `dispatchToolCall` without network or credentials, and the tool-disclosure contract (progressive tool loading, plan 041) without any provider call.
6
6
 
7
7
  Exported from `@arnilo/prism/testing/tool-conformance`:
8
8
 
9
9
  - `assertToolDispatchConforms(registry, options)`
10
+ - `assertToolDisclosureConforms(options)`
10
11
  - `assertToolBlocked(probe, expectedReason)`
11
12
  - `dispatchAndCollect(probe)`
12
- - `ToolConformanceOptions`, `ToolDispatchProbeOptions`
13
+ - `ToolConformanceOptions`, `ToolDispatchProbeOptions`, `ToolDisclosureConformanceOptions`
13
14
 
14
15
  ## When to use it
15
16
 
@@ -45,6 +46,29 @@ await assertToolDispatchConforms(createToolRegistry(), {
45
46
 
46
47
  `assertToolDispatchConforms` returns `Promise<void>` and throws on the first violation. `dispatchAndCollect` returns `{ result, events }` capturing the emitted `AgentEvent`s for custom assertions.
47
48
 
49
+ `assertToolDispatchConforms` returns `Promise<void>` and throws on the first violation. `dispatchAndCollect` returns `{ result, events }` capturing the emitted `AgentEvent`s for custom assertions.
50
+
51
+ ### Disclosure leg (toolsDisclosure "search")
52
+
53
+ `assertToolDisclosureConforms(options)` asserts the progressive tool-loading contract against the same narrowing the runtime applies (`filterTools` allow/deny bounds, then `selectDisclosedTools`):
54
+
55
+ - search mode only narrows: the disclosed set is a subset of the allow/deny-filtered input, never wider, never zero, deterministic order for identical turns; a deny-listed tool is never described to the provider
56
+ - the generated `search_tools` tool is always kept in the disclosed set
57
+ - fail closed: an index over the frozen 1024-tool cap discloses the full eligible list
58
+ - `search_tools` output is inert: names plus byte-truncated (512-char) descriptions only — no JSON structure, no tool schemas — oversized hosts descriptions are truncated, never executed, and configured `secrets` never surface even when a description carries one
59
+ - activation is bounded to topK and only ever selects from the eligible set; activated tools stay disclosed on the next turn
60
+
61
+ ```ts
62
+ import { assertToolDisclosureConforms } from "@arnilo/prism/testing/tool-conformance";
63
+
64
+ assertToolDisclosureConforms({
65
+ tools: hostTools, // incl. schema-bearing and oversized-description tools
66
+ filter: { deny: ["legacy_tool"] }, // host allow/deny bounds
67
+ search: { topK: 16 },
68
+ secrets: [hostSecret], // secret-scan of model-facing search output
69
+ });
70
+ ```
71
+
48
72
  ## Request/response example
49
73
 
50
74
  ```ts
@@ -84,5 +108,6 @@ await assertToolDispatchConforms(createToolRegistry(), {
84
108
  ## Related APIs
85
109
 
86
110
  - [Tools](tools.md)
111
+ - [Tool effects](tool-effects.md)
87
112
  - [Settings, auth, trust, security](settings-auth-trust-security.md)
88
113
  - [Provider conformance](provider-conformance.md)
package/docs/tools.md CHANGED
@@ -251,13 +251,40 @@ createJsonSchemaToolArgumentValidator({
251
251
  });
252
252
  ```
253
253
 
254
+ ## Tool disclosure (progressive tool loading)
255
+
256
+ `toolsDisclosure` on `AgentConfig` / `RunOptions` (run wins; default `"all"`) controls how active tools reach the provider request. Default `"all"` sends every active tool schema — byte-identical to releases before the option existed. Opt-in `"search"` surfaces a bounded top-k subset per turn (scored lexically against the turn input over name and description) plus the generated `search_tools` tool; the model requests more by calling it.
257
+
258
+ ```ts
259
+ const agent = createAgent({
260
+ model, provider,
261
+ tools, // host-active ToolDefinitions (or registry)
262
+ toolsDisclosure: "search", // default "all"
263
+ toolsSearch: { topK: 16 }, // optional; clamped to the hard cap
264
+ });
265
+ ```
266
+
267
+ Limits (mirroring the skill-disclosure DEFAULT/HARD cap pattern):
268
+
269
+ | Limit | Default | Hard cap |
270
+ | --- | --- | --- |
271
+ | Disclosed tools per turn (`topK`) | 16 | 64 |
272
+ | Indexed tools | — | 1024 (fail closed to full disclosure) |
273
+ | Search query bytes | 4096 | 65536 |
274
+
275
+ - `search_tools({ query, k? })` returns inert `name: short description [matched: …]` lines — no schemas or tool bodies — and marks returned tools active for the session. Activation is names-only in run persistence (`sessionState.activatedToolNames`, capped at 128 names) and inert for tools absent from the current registry; a host can reset it with `session.clearActivatedTools()`.
276
+ - Fail closed: any index or scoring error discloses the full input list — never zero tools, never wider than the input list. Exhausting the frozen 1024-tool index cap is surfaced the same way.
277
+ - Disclosure never grants access: dispatch re-checks registry membership and allow/deny (`unknown_tool` / `tool_denied`) on every call regardless of what was described. Search results are intersected with the disclosed list structurally — searched tools are only ever selected from that list, never widened.
278
+ - Scoring is BM25-lite lexical (name tokens weigh ×3, IDF from the registry): bounded, dependency-free, deterministic tie-breaks. ponytail ceiling: embedder-backed scoring via `@arnilo/prism-rag` if accuracy fixtures fall short.
279
+ - Cross-link: skills apply the same discipline to prompt text — see [Context and skills](context-and-skills.md).
280
+
254
281
  ## Guardrails
255
282
 
256
283
  `DispatchToolCallOptions.guardrails` evaluates `tool_input` after `tool_call` middleware normalization and before lookup, permission, validation, execution policy, or side effect. `tool_output` evaluates raw completed results before redaction, event emission, ledger rows, and transcript append. A block returns a blocked result; tripwire fails the enclosing run. See [Guardrails](guardrails.md).
257
284
 
258
285
  ## Related APIs
259
286
 
260
- - [OpenAPI tools adapter](openapi-tools.md): optional `@arnilo/prism-openapi-tools` `createOpenApiTools` — compile host-selected OpenAPI 3.1 operationIds into bounded `ToolDefinition`s (allow-list only, pinned origin, resolved/bounded schemas, approval + effect-store idempotency on mutations, bounded body/response/retries/pagination, host credential resolver, untrusted output).
287
+ - [OpenAPI tools adapter](openapi-tools.md): optional `@arnilo/prism-coding-tools/openapi` `createOpenApiTools` — compile host-selected OpenAPI 3.1 operationIds into bounded `ToolDefinition`s (allow-list only, pinned origin, resolved/bounded schemas, approval + effect-store idempotency on mutations, bounded body/response/retries/pagination, host credential resolver, untrusted output).
261
288
  - [Agent/session runtime](agent-session-runtime.md): dispatches complete provider tool calls through the host-active tool harness and returns tool results on the next provider turn.
262
289
  - [Public contracts](public-contracts.md): `ToolDefinition`, `ToolRegistry`, `ToolExecutionContext`, `ToolResult`, and tool `AgentEvent` contracts.
263
290
  - [Contribution registries](contribution-registries.md): inert extension/package tool contribution storage.
@@ -270,6 +297,6 @@ createJsonSchemaToolArgumentValidator({
270
297
  - [MCP client bridge](mcp-tools.md): optional remote tool mapping plus separate bounded resource/prompt facades; non-tool MCP capabilities never bypass tool dispatch by masquerading as `ToolDefinition`.
271
298
  - [Recoverable tool effects](tool-effects.md): optional `tool.effect` + `effectStore` claim/CAS recovery around dispatch.
272
299
  - [Recoverable tool effects](tool-effects.md): optional `tool.effect` + `effectStore` claim/CAS recovery around dispatch.
273
- - [Coding agent tools](coding-agent-tools.md): optional first-party `@arnilo/prism-coding-agent` `shell`/`read`/`write`/`edit` tools a host registers into this harness.
300
+ - [Coding agent tools](coding-agent-tools.md): optional first-party `@arnilo/prism-coding-tools/agent` `shell`/`read`/`write`/`edit` tools a host registers into this harness.
274
301
 
275
302
  `DispatchToolCallOptions.trust` and `.permission` run before validation or `execute()`; denial emits `tool_execution_blocked`. Middleware cannot bypass either guard. `AgentConfig.validator`/`RunOptions.validate` run after these guards; their output is redacted through the active `SecretRedactor`. `createSecureAgent()` requires all three seams plus non-empty schemas and durable pre-tool approval. Prism does not sandbox tools. See [Security/auth/trust](settings-auth-trust-security.md).
package/docs/web-tools.md CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  Use when agent needs explicit public-web discovery or host-approved document retrieval/extraction. Keep search separate from fetch/extract so model cannot select provider, credential, API origin, extraction schema, or cost path.
10
10
 
11
- **Obscura-backed alternative**: the optional [`@arnilo/prism-obscura`](obscura.md) package provides `web_search`/`web_fetch` behavior backed by a host-installed Obscura headless browser through its CLI (one replaceable HTML search profile instead of an API key), plus explicit native `obscura_fetch`/`obscura_scrape` batch tools. It reuses this package's normalized citation/untrusted shapes (`provider: "obscura"`) but does not require credentials; the API-backed Brave/Exa/Firecrawl adapters here remain the preferred path when an API key is available.
11
+ **Obscura-backed alternative**: the optional `@arnilo/prism-web-tools/obscura` subpath ([Obscura](obscura.md)) provides `web_search`/`web_fetch` behavior backed by a host-installed Obscura headless browser through its CLI (one replaceable HTML search profile instead of an API key), plus explicit native `obscura_fetch`/`obscura_scrape` batch tools. It reuses this package's normalized citation/untrusted shapes (`provider: "obscura"`) but does not require credentials; the API-backed Brave/Exa/Firecrawl adapters here remain the preferred path when an API key is available.
12
12
 
13
13
  ## Inputs / request
14
14
 
@@ -69,7 +69,7 @@ Default/hard limits: query 4/16 KiB; results 10/20; URLs 5/20; request 256 KiB/1
69
69
 
70
70
  Provider credentials never enter tool schemas/results, prompts, telemetry, URLs, or errors. Error text excludes remote bodies. Search snippets, Markdown, and extracted JSON are prompt-injection-capable data: never concatenate them into system instructions or use them to modify tools, permissions, credentials, trust, routing, or schemas. Firecrawl fetches target URLs remotely; Prism cannot claim target DNS pinning after handoff. Use controlled host fetch when that guarantee is required.
71
71
 
72
- Default tests use injected fake fetch and make no public request. Restricted smoke: `PRISM_LIVE_WEB=1 npm run test:live -w @arnilo/prism-web-tools` plus least-privilege provider environment credential. Prefer these tools over `@arnilo/prism-browser` for ordinary public retrieval; use browser automation only for interactive/authenticated/JavaScript-heavy work behind a host egress proxy. Arbitrary HTML execution, model-selected providers, automatic OAuth forwarding, and generic web/MCP passthrough are unsupported.
72
+ Default tests use injected fake fetch and make no public request. Restricted smoke: `PRISM_LIVE_WEB=1 npm run test:live -w @arnilo/prism-web-tools` plus least-privilege provider environment credential. Prefer the [`browser`](browser-automation.md) subpath over ordinary public retrieval; use browser automation only for interactive/authenticated/JavaScript-heavy work behind a host egress proxy. Arbitrary HTML execution, model-selected providers, automatic OAuth forwarding, and generic web/MCP passthrough are unsupported.
73
73
 
74
74
  ## Related APIs
75
75
 
package/docs/wiki.md CHANGED
@@ -1,8 +1,8 @@
1
- # LLM Wiki (@arnilo/prism-wiki)
1
+ # LLM Wiki (@arnilo/prism-memory/wiki)
2
2
 
3
3
  ## What it does
4
4
 
5
- `@arnilo/prism-wiki` implements Andrej Karpathy's **LLM Wiki Pattern** for the Prism agent ecosystem. It acts as a knowledge compiler that transforms raw, immutable sources (source code, AST symbols, notes, markdown clips, transcripts, journal entries) into a persistent, compounding, cross-linked Markdown knowledge base (`.wiki/`).
5
+ The `@arnilo/prism-memory/wiki` subpath implements Andrej Karpathy's **LLM Wiki Pattern** for the Prism agent ecosystem. It acts as a knowledge compiler that transforms raw, immutable sources (source code, AST symbols, notes, markdown clips, transcripts, journal entries) into a persistent, compounding, cross-linked Markdown knowledge base (`.wiki/`).
6
6
 
7
7
  It integrates Tobias Lütke's [`qmd`](https://github.com/tobi/qmd) on-device hybrid search engine (BM25, vector search, and LLM reranking) and hydrates search results with Context7-inspired hierarchical breadcrumbs (`# Category > ## Topic`) and live clickable source line anchors (`file:///path/to/file#Lxx-Lyy` format), enabling agents and humans to navigate code and notes directly without blind regex loops (`grep`/`rg`).
8
8
 
@@ -98,7 +98,7 @@ The authentication layer uses asymmetric Ed25519 JWT verification in middleware,
98
98
 
99
99
  ```ts
100
100
  import { createExtensionKernel } from "@arnilo/prism";
101
- import { createWikiExtension, initWiki, refreshWiki, lintWiki } from "@arnilo/prism-wiki";
101
+ import { createWikiExtension, initWiki, refreshWiki, lintWiki } from "@arnilo/prism-memory/wiki";
102
102
 
103
103
  const kernel = createExtensionKernel();
104
104
 
@@ -112,7 +112,7 @@ await kernel.load([wiki]);
112
112
 
113
113
  ## Skills and Auto-Deployment
114
114
 
115
- `@arnilo/prism-wiki` includes two specialized skills formatted according to `.agents/skills/skill-creator`:
115
+ The wiki subpath includes two specialized skills formatted according to `.agents/skills/skill-creator`:
116
116
 
117
117
  1. **`wiki-maintainer`**: Ingestion, compilation, line-anchor validation, and contradiction reconciliation rules.
118
118
  2. **`wiki-searcher`**: Context7 hierarchical breadcrumb query resolution, zero-grep instructions, and compounding insight recording.
@@ -135,7 +135,7 @@ Emitted `.wiki/` trees are [OKF v0.2](https://github.com/GoogleCloudPlatform/ope
135
135
 
136
136
  ## Extension and configuration notes
137
137
 
138
- - `@arnilo/prism-wiki` registers tools (`wiki_search`, `wiki_read_page`, `wiki_record_insight`), commands (`wiki-init`, `wiki-refresh`, `wiki-lint`), skills (`wiki-maintainer`, `wiki-searcher`), and instruction injectors (`wiki-guidance`) into Prism registries.
138
+ - The wiki subpath registers tools (`wiki_search`, `wiki_read_page`, `wiki_record_insight`), commands (`wiki-init`, `wiki-refresh`, `wiki-lint`), skills (`wiki-maintainer`, `wiki-searcher`), and instruction injectors (`wiki-guidance`) into Prism registries.
139
139
  - It operates with zero core modifications and can be used with any `@arnilo/prism` agent.
140
140
  - `qmd` is optional but recommended. When `@tobilu/qmd` is not installed, the search engine falls back to catalog matching against `index.md`.
141
141
 
@@ -148,7 +148,7 @@ Emitted `.wiki/` trees are [OKF v0.2](https://github.com/GoogleCloudPlatform/ope
148
148
 
149
149
  ## Related APIs
150
150
 
151
- - [`@arnilo/prism-rag`](rag.md): Bounded document chunking and vector context injection.
151
+ - [`@arnilo/prism-memory/rag`](rag.md): Bounded document chunking and vector context injection.
152
152
  - [`@arnilo/prism-memory`](working-and-semantic-memory.md): Embedder and VectorStore primitives.
153
153
  - [`@arnilo/prism-coding-agent`](coding-agent-tools.md): Code manipulation and reading tools.
154
154
  - [`Contribution registries`](contribution-registries.md): Extension contribution model.
@@ -12,6 +12,8 @@ Interactive TUI (**C-012**) is **out of scope** for Plan 057 and deferred. Workf
12
12
 
13
13
  **Phase 11 addendum (2026-07-16):** schedules use a separate generic checkpoint namespace plus per-fire `LeaseStore` claims and deterministic queued-run IDs; SQLite/PostgreSQL need no workflow-specific table or migration. Background execution remains `enqueueWorkflow` + the existing coordinator. Nested workflow nodes call the same runner with inherited policy/ownership/checkpoint/event seams. Shared JSON state is validated, redacted, byte/history bounded, and checkpointed by version. Replay creates a new checkpoint with immutable source lineage and copied terminal evidence; approval-bearing prior paths cannot be copied.
14
14
 
15
+ **Plan 045 Task 2 addendum (2026-08-31):** loop nodes keep one acyclic graph node while durable checkpoints append bounded, versioned iteration records. A tool sub-step can suspend before side effects; approved resume re-enters only the incomplete iteration. `node_iteration_started` / `node_iteration_finished` expose stable iteration IDs and bounded/redacted outputs. Replay starts a fresh cursor and emits new iteration events. A host saga treats the loop as one aggregate step and compensates its iteration IDs in reverse order; no workflow-specific SQL or implicit saga coupling is added.
16
+
15
17
  ## When to use it
16
18
 
17
19
  - **Workflow package authors** should start here, then follow [Agent/session runtime](agent-session-runtime.md), [Agent loops](agent-loops.md), [Runs and usage ledger](runs-and-usage.md), [CLI/RPC](cli-rpc.md), and [Database persistence](database-persistence.md).
@@ -139,6 +141,7 @@ import type { OwnershipScope, SecretRedactor } from "@arnilo/prism";
139
141
 
140
142
  /** Schema version for checkpoint payload layout (package-owned). */
141
143
  export const WORKFLOW_CHECKPOINT_SCHEMA_VERSION = 1 as const;
144
+ export const WORKFLOW_LOOP_ITERATION_SCHEMA_VERSION = 1 as const;
142
145
 
143
146
  export type WorkflowRunStatus =
144
147
  | "queued"
@@ -158,6 +161,16 @@ export interface WorkflowNodeCheckpoint {
158
161
  readonly sessionId?: string;
159
162
  readonly leafId?: string;
160
163
  readonly runId?: string;
164
+ /** Optional additive loop cursor/ledger; absent on legacy checkpoints. */
165
+ readonly iteration?: number;
166
+ readonly lastOutput?: unknown;
167
+ readonly iterations?: readonly {
168
+ readonly schemaVersion: typeof WORKFLOW_LOOP_ITERATION_SCHEMA_VERSION;
169
+ readonly iteration: number;
170
+ readonly iterationId: string;
171
+ readonly done: boolean;
172
+ readonly output?: unknown;
173
+ }[];
161
174
  }
162
175
 
163
176
  export interface WorkflowCheckpointValue {
@@ -259,6 +272,17 @@ export type WorkflowEvent =
259
272
  | { readonly type: "workflow_finished"; readonly workflowId: string; readonly runId: string; readonly status: WorkflowRunStatus; readonly timestamp: string }
260
273
  | { readonly type: "node_started"; readonly workflowId: string; readonly runId: string; readonly nodeId: string; readonly timestamp: string }
261
274
  | { readonly type: "node_finished"; readonly workflowId: string; readonly runId: string; readonly nodeId: string; readonly timestamp: string }
275
+ | {
276
+ readonly type: "node_iteration_started" | "node_iteration_finished";
277
+ readonly workflowId: string;
278
+ readonly runId: string;
279
+ readonly nodeId: string;
280
+ readonly iteration: number;
281
+ readonly iterationId: string;
282
+ readonly done?: boolean;
283
+ readonly output?: unknown;
284
+ readonly timestamp: string;
285
+ }
262
286
  | { readonly type: "node_failed"; readonly workflowId: string; readonly runId: string; readonly nodeId: string; readonly error: { readonly message: string; readonly code?: string | number }; readonly timestamp: string }
263
287
  | { readonly type: "node_skipped"; readonly workflowId: string; readonly runId: string; readonly nodeId: string; readonly reason?: string; readonly timestamp: string }
264
288
  | { readonly type: "checkpoint_saved"; readonly workflowId: string; readonly runId: string; readonly version: number; readonly timestamp: string }