stitchkit 0.115.0 → 0.117.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +67 -0
- package/dist/agent-runtime/coding-tool-paths.d.ts.map +1 -1
- package/dist/agent-runtime/run-acquisition.d.ts +9 -2
- package/dist/agent-runtime/run-acquisition.d.ts.map +1 -1
- package/dist/agent-runtime/run-execution.d.ts +3 -0
- package/dist/agent-runtime/run-execution.d.ts.map +1 -1
- package/dist/agent-runtime/run-failure.d.ts +4 -1
- package/dist/agent-runtime/run-failure.d.ts.map +1 -1
- package/dist/agent-runtime/run-store-retry.d.ts +8 -0
- package/dist/agent-runtime/run-store-retry.d.ts.map +1 -0
- package/dist/agent-runtime/run-terminal.d.ts.map +1 -1
- package/dist/agent-runtime/run-unacquired.d.ts +19 -0
- package/dist/agent-runtime/run-unacquired.d.ts.map +1 -0
- package/dist/agent-runtime/store-conformance.d.ts +8 -0
- package/dist/agent-runtime/store-conformance.d.ts.map +1 -1
- package/dist/agent-runtime/store-driver-contract.d.ts +5 -0
- package/dist/agent-runtime/store-driver-contract.d.ts.map +1 -1
- package/dist/agent-runtime/terminal-commit.d.ts +34 -4
- package/dist/agent-runtime/terminal-commit.d.ts.map +1 -1
- package/dist/entrypoints/agent-runtime/coding-tools.js +4 -4
- package/dist/entrypoints/agent-runtime/harness.js +4 -4
- package/dist/entrypoints/agent-runtime/sandbox.js +4 -4
- package/dist/entrypoints/agent-runtime/sqlite/bun.js +7 -7
- package/dist/entrypoints/agent-runtime/sqlite/node.js +7 -7
- package/dist/entrypoints/agent-runtime/testing.js +26 -8
- package/dist/entrypoints/agent-runtime.d.ts +1 -1
- package/dist/entrypoints/agent-runtime.d.ts.map +1 -1
- package/dist/entrypoints/agent-runtime.js +17 -15
- package/dist/entrypoints/application/diagnostic-journal.js +1 -1
- package/dist/entrypoints/application/directory-inbox.js +3 -3
- package/dist/entrypoints/cli/publish.js +2 -2
- package/dist/entrypoints/cli.js +3 -3
- package/dist/entrypoints/files.js +2 -2
- package/dist/entrypoints/primitives.js +51 -154
- package/dist/entrypoints/server.js +6 -6
- package/dist/entrypoints/telegram/grammy.js +2 -2
- package/dist/entrypoints/telegram.d.ts +1 -1
- package/dist/entrypoints/telegram.d.ts.map +1 -1
- package/dist/entrypoints/telegram.js +203 -70
- package/dist/entrypoints/tools.js +3 -3
- package/dist/entrypoints/tracking/contract.d.ts +11 -0
- package/dist/entrypoints/tracking/contract.d.ts.map +1 -0
- package/dist/entrypoints/tracking/contract.js +162 -0
- package/dist/entrypoints/tracking/performance.d.ts +8 -0
- package/dist/entrypoints/tracking/performance.d.ts.map +1 -0
- package/dist/entrypoints/tracking/performance.js +181 -0
- package/dist/entrypoints/tracking/server.d.ts +1 -0
- package/dist/entrypoints/tracking/server.d.ts.map +1 -1
- package/dist/entrypoints/tracking/server.js +40 -12
- package/dist/entrypoints/tracking.d.ts +4 -3
- package/dist/entrypoints/tracking.d.ts.map +1 -1
- package/dist/entrypoints/tracking.js +43 -143
- package/dist/entrypoints/xlsx.d.ts +4 -0
- package/dist/entrypoints/xlsx.d.ts.map +1 -0
- package/dist/entrypoints/xlsx.js +406 -0
- package/dist/{index-crmswbrt.js → index-073nw9qr.js} +8 -8
- package/dist/index-1f40z584.js +7 -0
- package/dist/{index-nregbbsc.js → index-6f49krb5.js} +1 -1
- package/dist/{index-fdk4r4r6.js → index-75ns2ybj.js} +1 -1
- package/dist/{index-x0ddbj85.js → index-arjmb6de.js} +36 -1
- package/dist/{index-944x38nv.js → index-cycp3r3e.js} +125 -182
- package/dist/{index-dx5cm9pc.js → index-ghtpfz4z.js} +9 -9
- package/dist/{index-n3v695sb.js → index-hdjmdwsj.js} +277 -96
- package/dist/{index-7yx8j6ay.js → index-n5sv4z0w.js} +12 -3
- package/dist/{index-zqkz4af9.js → index-nqxrm176.js} +1 -1
- package/dist/{index-j8kf56wx.js → index-q968hy6y.js} +1 -1
- package/dist/index-qezhmp51.js +242 -0
- package/dist/index-vv1sch8a.js +94 -0
- package/dist/{index-3qqdqwxe.js → index-w7jq3fz9.js} +25 -5
- package/dist/{index-fggxgbrk.js → index-xx1j310k.js} +165 -102
- package/dist/internal/contained-files.d.ts +10 -0
- package/dist/internal/contained-files.d.ts.map +1 -1
- package/dist/internal/exclusive-lock.d.ts.map +1 -1
- package/dist/primitives/decimal.d.ts +10 -0
- package/dist/primitives/decimal.d.ts.map +1 -1
- package/dist/primitives/index.d.ts +3 -1
- package/dist/primitives/index.d.ts.map +1 -1
- package/dist/primitives/money.d.ts +20 -0
- package/dist/primitives/money.d.ts.map +1 -1
- package/dist/primitives/quantity.d.ts.map +1 -1
- package/dist/primitives/rounding.d.ts +40 -0
- package/dist/primitives/rounding.d.ts.map +1 -0
- package/dist/telegram/budget-transformer.d.ts +6 -3
- package/dist/telegram/budget-transformer.d.ts.map +1 -1
- package/dist/telegram/not-dispatched.d.ts +2 -2
- package/dist/telegram/not-dispatched.d.ts.map +1 -1
- package/dist/telegram/outbound-budget.d.ts +72 -20
- package/dist/telegram/outbound-budget.d.ts.map +1 -1
- package/dist/telegram/outbound-cost.d.ts +5 -0
- package/dist/telegram/outbound-cost.d.ts.map +1 -0
- package/dist/telegram/outbound-pauses.d.ts +17 -0
- package/dist/telegram/outbound-pauses.d.ts.map +1 -0
- package/dist/telegram/outbound-queue.d.ts +17 -0
- package/dist/telegram/outbound-queue.d.ts.map +1 -0
- package/dist/tools/cli/view.d.ts +7 -1
- package/dist/tools/cli/view.d.ts.map +1 -1
- package/dist/tracking/attribution.d.ts +1 -1
- package/dist/tracking/attribution.d.ts.map +1 -1
- package/dist/tracking/client-visit.d.ts +3 -1
- package/dist/tracking/client-visit.d.ts.map +1 -1
- package/dist/tracking/client.d.ts.map +1 -1
- package/dist/tracking/performance/collector.d.ts +61 -0
- package/dist/tracking/performance/collector.d.ts.map +1 -0
- package/dist/tracking/performance/schema.d.ts +45 -0
- package/dist/tracking/performance/schema.d.ts.map +1 -0
- package/dist/tracking/performance/summary.d.ts +12 -0
- package/dist/tracking/performance/summary.d.ts.map +1 -0
- package/dist/tracking/privacy.d.ts +5 -0
- package/dist/tracking/privacy.d.ts.map +1 -0
- package/dist/tracking/server/telemetry-health.d.ts +31 -0
- package/dist/tracking/server/telemetry-health.d.ts.map +1 -0
- package/dist/xlsx/cells.d.ts +17 -0
- package/dist/xlsx/cells.d.ts.map +1 -0
- package/dist/xlsx/render.d.ts +11 -0
- package/dist/xlsx/render.d.ts.map +1 -0
- package/dist/xlsx/schema.d.ts +119 -0
- package/dist/xlsx/schema.d.ts.map +1 -0
- package/dist/xlsx/zip.d.ts +13 -0
- package/dist/xlsx/zip.d.ts.map +1 -0
- package/llms/agent-runtime.2.txt +40 -16
- package/llms/agent-runtime.3.txt +17 -0
- package/llms/agent-runtime.txt +6 -3
- package/llms/cli.txt +3 -1
- package/llms/primitives.txt +21 -0
- package/llms/stitchkit.txt +9 -6
- package/llms/telegram.txt +39 -12
- package/llms/tracking-contract.txt +32 -0
- package/llms/tracking-performance.txt +21 -0
- package/llms/tracking-server.txt +1 -0
- package/llms/tracking.txt +75 -12
- package/llms/{upgrading-0.104.0-0.115.0.txt → upgrading-0.105.0-0.117.0.txt} +73 -124
- package/llms/{upgrading-0.44.0-0.61.0.txt → upgrading-0.44.0-0.62.0.txt} +120 -3
- package/llms/{upgrading-0.62.0-0.78.0.txt → upgrading-0.63.0-0.83.0.txt} +181 -120
- package/llms/{upgrading-0.79.0-0.103.13.txt → upgrading-0.84.0-0.104.0.txt} +124 -181
- package/llms/upgrading.txt +4 -4
- package/llms/xlsx.txt +107 -0
- package/llms-full.txt +390 -34
- package/llms.txt +15 -11
- package/package.json +27 -9
- package/dist/{index-z3d6smer.js → index-tdpmfks8.js} +5 -5
package/llms-full.txt
CHANGED
|
@@ -52,7 +52,7 @@ own, recorded as an ADR.
|
|
|
52
52
|
| `stitchkit/realtime` | browser, React Native/Hermes **and** server | evolving | typed realtime clients and retained topics with a required explicit Socket.IO peer loader |
|
|
53
53
|
| `stitchkit/contract` | browser **and** server | stable | the contract layer alone — `defineContract`, errors, pagination |
|
|
54
54
|
| `stitchkit/live` | browser **and** server | evolving | `defineEvents` — topic declarations beside the operation contract — and watched reads: `createWatchClient`, the value/difference frame and its reassembly |
|
|
55
|
-
| `stitchkit/primitives` | browser **and** server | evolving | generic values and declarations for lifecycle, access, audit, delivery and exports |
|
|
55
|
+
| `stitchkit/primitives` | browser **and** server | evolving | generic values and declarations for lifecycle, access, audit, delivery and exports, exact money and rounding tables |
|
|
56
56
|
| `stitchkit/server` | server (Bun) | stable | `createServer`, `implement`, hooks, auth, Socket.IO server, server primitives |
|
|
57
57
|
| `stitchkit/node` | server (Node ≥ 22) | stable | `serveNode` + the runtime-agnostic core — the Node mirror of `/server` |
|
|
58
58
|
| `stitchkit/tools` | server | stable | `createMcpHandler`, `mountMcp`, `mountAgent`, the OAuth provider, native tools |
|
|
@@ -67,24 +67,27 @@ own, recorded as an ADR.
|
|
|
67
67
|
| `stitchkit/process/owner-loss` | bundled server entry (Bun, POSIX) | evolving | narrow bootstrap that keeps the process runner lazy while carrying its owner-loss guard in a single-file bundle |
|
|
68
68
|
| `stitchkit/files/packaging` | build time (Bun or Node) | evolving | installed native asset graph, integrity, Bun packaging plugin and the artifact loader check; no runtime install policy |
|
|
69
69
|
| `stitchkit/files` | server (Bun or Node) | stable | peer-free managed local-file boundary |
|
|
70
|
-
| `stitchkit/telegram` | server (Bun or Node) | evolving | peer-free Telegram platform primitives — Mini App `initData` verification, send-failure classification, one outbound budget per bot token with weighted albums and
|
|
71
|
-
| `stitchkit/telegram/grammy` | server (Bun or Node) | evolving | the token's outbound budget as one grammY client transformer — a 429 that comes back as a response pauses
|
|
70
|
+
| `stitchkit/telegram` | server (Bun or Node) | evolving | peer-free Telegram platform primitives — Mini App `initData` verification, send-failure classification, one outbound budget per bot token with weighted albums and per-chat 429 pauses, resumable broadcasts, the operator channel with dedupe, a Bot API transport that separates a request that never left from an unknown outcome, local Bot API files, webhook ownership and durable webhook update intake |
|
|
71
|
+
| `stitchkit/telegram/grammy` | server (Bun or Node) | evolving | the token's outbound budget as one grammY client transformer — a 429 that comes back as a response pauses its chat, and a cancelled wait is a request that never left |
|
|
72
72
|
| `stitchkit/telegram/html` | browser and server | evolving | Telegram's HTML as a tree — clean any markup to what Telegram accepts, cut long text into valid parts by what the reader sees, check markup before it is sent |
|
|
73
73
|
| `stitchkit/telegram/screens` | server (Bun or Node) | evolving | a bot's menus as declared screens over grammY — typed paths, actions and input, message reconciliation, state in any `StorageAdapter`, a test chat |
|
|
74
74
|
| `stitchkit/tracking` | browser **and** server | evolving | visitor-tracking mechanics — `createTrackingClient`, the tab-shared outbox, the page-leave beacon, attribution, the contract factory; no event vocabulary, no React |
|
|
75
|
+
| `stitchkit/tracking/contract` | browser **and** server | evolving | the wire shapes — the contract factory, event and visit schemas, the performance sample and its trust threshold; brings `zod` |
|
|
76
|
+
| `stitchkit/tracking/performance` | browser **and** server | evolving | Web Vitals from real visitors, collected after load through the optional peer `web-vitals`; no `zod` |
|
|
75
77
|
| `stitchkit/tracking/server` | server (Bun or Node) | evolving | the decisions a tracking backend makes — dispositions, visit lease over an application-owned store, active intervals, presence; no database |
|
|
76
78
|
| `stitchkit/release` | browser **and** server | evolving | a page follows the release it was built for — `createReleaseMarker` on the server, `createReleaseWatcher` in the browser, the `X-Build-Id` header and a socket event between them |
|
|
77
79
|
| `stitchkit/voice` | browser **and** server | evolving | the mechanics of a live voice reply — `SentenceCutter`, `speakableText`, a `SpeechQueue` that synthesises ahead and stops on interruption, the conversation phases |
|
|
78
80
|
| `stitchkit/geo` | server (Bun or Node) | evolving | managed GeoIP reader generations, last-known-good reload and the optional MaxMind adapter |
|
|
81
|
+
| `stitchkit/xlsx` | browser **and** server | evolving | a workbook from rows and typed columns, with no dependency; text never runs as a formula |
|
|
79
82
|
| `stitchkit/oauth` | browser **and** server | evolving | provider-neutral Authorization Code + PKCE request and one-shot callback transaction mechanics |
|
|
80
83
|
| `stitchkit/google` | server (Bun or Node) | evolving | optional Google code exchange and verified OIDC identity adapter |
|
|
81
|
-
| `stitchkit/observability` | server | stable<br>_redefined in 2 of the
|
|
84
|
+
| `stitchkit/observability` | server | stable<br>_redefined in 2 of the 62 minors since 0.56.2, most recently 0.92.0_ | request/tool event projections — `createObservability`, trace context, sanitisation |
|
|
82
85
|
| `stitchkit/testing` | tests on Bun or Node | stable | in-process generated clients over a real Fetch handler, the managed-resource conformance kit and manual time for schedules and retries |
|
|
83
86
|
| `stitchkit/declaration` | browser + build and deployment tooling (Bun or Node) | evolving | `ProjectDeclarationSchema` — the one machine-readable statement a repository makes about itself |
|
|
84
87
|
| `stitchkit/react` | browser + server rendering | stable | `createCursorQuery`, `createCacheBridge`, QueryClient and `ApiError` retry policy |
|
|
85
88
|
| `stitchkit/react/keyboard` | browser | evolving | which part of a screen gets a key — ordered layers, one Escape per level, list zones with real focus; see [keyboard guide](keyboard.md) |
|
|
86
89
|
| `stitchkit/react/keyboard/testing` | server (Bun or Node) | evolving | the keyboard layers and zones without a DOM — fakes for the window, elements and key events beside the two engines, to test the order of layers, `scope`/`inert` and the edges of a zone in milliseconds |
|
|
87
|
-
| `stitchkit/agent-runtime` | server | evolving<br>_redefined in 25 of the
|
|
90
|
+
| `stitchkit/agent-runtime` | server | evolving<br>_redefined in 25 of the 62 minors since 0.56.2, most recently 0.115.0_ | optional durable conversation/run loop, history, models, prompts, fencing and events |
|
|
88
91
|
| `stitchkit/agent-runtime/sandbox` | server | evolving | optional Linux Bubblewrap sessions, durable workspaces and a host HTTP credential gateway; see [sandbox guide](sandbox.md) |
|
|
89
92
|
| `stitchkit/agent-runtime/testing` | tests on Bun or Node | evolving | credential-free replay, scripted provider faults, deterministic race controls and the agent-store conformance kit |
|
|
90
93
|
| `stitchkit/agent-runtime/harness` | server | evolving | resource-aware process-local facade over the canonical Agent runtime; supervision stays outside |
|
|
@@ -96,7 +99,7 @@ own, recorded as an ADR.
|
|
|
96
99
|
| `stitchkit/agent-runtime/sqlite/bun` | server (Bun) | evolving | durable built-in SQLite store for the agent runtime |
|
|
97
100
|
| `stitchkit/agent-runtime/sqlite/node` | server (Node ≥ 22.5) | evolving | durable built-in SQLite store for the agent runtime |
|
|
98
101
|
| `stitchkit-tui` | terminal (Bun) | evolving | optional official OpenTUI host over a caller-composed headless runtime |
|
|
99
|
-
| `stitchkit/application` | browser + server | evolving<br>_redefined in 14 of the
|
|
102
|
+
| `stitchkit/application` | browser + server | evolving<br>_redefined in 14 of the 62 minors since 0.56.2, most recently 0.110.0_ | managed resource graph, readiness, admission, schedules, subtree restart and bounded shutdown |
|
|
100
103
|
| `stitchkit/application/grammy` | server | evolving | a grammY bot as application resources — configuration, then polling with updates admitted by batch — and the webhook adapter |
|
|
101
104
|
| `stitchkit/application/opentelemetry` | server | evolving | maps application snapshots onto an injected OpenTelemetry `Meter` |
|
|
102
105
|
| `stitchkit/application/schemas` | browser + server | evolving | the application's snapshot, health and shutdown schemas alone, without the kernel |
|
|
@@ -448,7 +451,7 @@ Both entrypoints are **evolving**.
|
|
|
448
451
|
## Contract
|
|
449
452
|
|
|
450
453
|
```ts
|
|
451
|
-
import { createTrackingContract } from 'stitchkit/tracking'
|
|
454
|
+
import { createTrackingContract } from 'stitchkit/tracking/contract'
|
|
452
455
|
|
|
453
456
|
export const tracking = createTrackingContract({
|
|
454
457
|
scope: 'public',
|
|
@@ -647,6 +650,76 @@ has an identity. A bot gets a lease that no store holds, so its events are
|
|
|
647
650
|
`createPresenceRegistry` is who is here now, in this process: touch it from
|
|
648
651
|
`track`, read it for a live feed. It is honestly empty after a restart.
|
|
649
652
|
|
|
653
|
+
## Real-visitor speed
|
|
654
|
+
|
|
655
|
+
`stitchkit/tracking/performance` collects Web Vitals from the people who use the site. Install the
|
|
656
|
+
optional peer `web-vitals` and call it once on the page:
|
|
657
|
+
|
|
658
|
+
```ts
|
|
659
|
+
import { createPerformanceCollector } from 'stitchkit/tracking/performance'
|
|
660
|
+
|
|
661
|
+
createPerformanceCollector({
|
|
662
|
+
buildId: BUILD_ID,
|
|
663
|
+
url: '/api/tracking/performance',
|
|
664
|
+
visitId: () => client.visitId(),
|
|
665
|
+
})
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
The library is fetched as its own chunk after `load`, when the browser is idle, so the measurement costs
|
|
669
|
+
the page nothing it is measuring. Each load posts an `early` sample five seconds later and a `final` one
|
|
670
|
+
as the page goes, both with `sendBeacon` and the same `id`: keep the later one. The body is a string, so
|
|
671
|
+
the endpoint declares `safelistedBody: true`. A sample that could not leave waits in session storage for the
|
|
672
|
+
next page, up to 20 of them, and of one load only the newest.
|
|
673
|
+
|
|
674
|
+
A browser under automation sends nothing, and neither does an agent that admits to being Lighthouse,
|
|
675
|
+
headless Chrome, GTmetrix or a crawler. Lighthouse with its default settings imitates an ordinary browser, so a
|
|
676
|
+
local run with `webdriver` off cannot be told from a visitor.
|
|
677
|
+
|
|
678
|
+
One load is one document: a client-side navigation stays under the route the page opened on, and whatever
|
|
679
|
+
`web-vitals` reports after a back-forward restore is left out, because it would replace the load's numbers
|
|
680
|
+
with a restore's. A second call on a page returns the running collector.
|
|
681
|
+
|
|
682
|
+
A sample carries the route, the build, `mobile` or `desktop`, and the five metrics. **A metric that did not
|
|
683
|
+
arrive is `null`, never zero**: most visits have no INP at all, and a zero would pull every average down.
|
|
684
|
+
|
|
685
|
+
Parse a sample on your own endpoint and store it in your own table:
|
|
686
|
+
|
|
687
|
+
```ts
|
|
688
|
+
import { redactTrackingText } from 'stitchkit/tracking'
|
|
689
|
+
import { PerformanceSampleSchema, summarizePerformanceMetric } from 'stitchkit/tracking/contract'
|
|
690
|
+
|
|
691
|
+
const sample = PerformanceSampleSchema.parse(await request.json())
|
|
692
|
+
await samples.upsert({ ...sample, route: redactTrackingText(sample.route) })
|
|
693
|
+
|
|
694
|
+
const lcp = summarizePerformanceMetric(rows.map((row) => row.vitals.lcpMs))
|
|
695
|
+
// { samples: 42, p75: 1840, p95: null } — a p95 needs 100 samples
|
|
696
|
+
```
|
|
697
|
+
|
|
698
|
+
The report is yours: which routes matter and what is slow are the application's words.
|
|
699
|
+
|
|
700
|
+
## Privacy
|
|
701
|
+
|
|
702
|
+
`redactTrackingText` masks passwords, sign-in credentials, API keys, bearer tokens and signed Telegram launch
|
|
703
|
+
data in a URL or free text, and leaves paths and campaign parameters byte for byte. Text past 16 KiB is cut
|
|
704
|
+
first, because the endpoint is open to anyone. `code` and `hash` count only beside the
|
|
705
|
+
`state` or `auth_date` that makes them credentials, so a promo code stays. `redactTrackingValue` applies the
|
|
706
|
+
same rule through a value of any shape. The client applies it to the landing path, the referrer and the
|
|
707
|
+
campaign tags before sending or storing them. Apply it again on your ingest: anyone can post to that endpoint without running the client.
|
|
708
|
+
|
|
709
|
+
## Is tracking losing events?
|
|
710
|
+
|
|
711
|
+
`trackingTelemetryHealth(reports, baselines)` reads the outbox figures of a visit's bootstrap
|
|
712
|
+
(`browserStreamId` as `stream`, `outboxState`, `outboxQueued`, `outboxDropped`) and answers `ok`, `degraded` or
|
|
713
|
+
`unknown`. An application running without an outbox reports `unavailable` and reads as `degraded`. A browser repeats its lifetime `dropped` with
|
|
714
|
+
every visit, so pass what each stream reported before the window as `baselines`, and only the growth counts.
|
|
715
|
+
|
|
716
|
+
## Bundle weight
|
|
717
|
+
|
|
718
|
+
`stitchkit/tracking` has no `zod` in its graph: `createTrackingClient` with `indexedDbOutboxStorage` is under
|
|
719
|
+
8 KB gzip, a test holds that, and the build refuses a published file that reaches `zod`. The schemas live in
|
|
720
|
+
`stitchkit/tracking/contract` for the server and a shared contract. Importing that in code a browser loads
|
|
721
|
+
brings `zod` back, so keep a shared contract out of the storefront's client bundle.
|
|
722
|
+
|
|
650
723
|
## Not here
|
|
651
724
|
|
|
652
725
|
GeoIP, event labels, funnels, reports, the analytics page, Socket.IO push into
|
|
@@ -5671,8 +5744,8 @@ stay pinned through authorization; `write_file` plans before it and opens the pa
|
|
|
5671
5744
|
the parent it creates does not exist yet. Reads revalidate the pinned file identity;
|
|
5672
5745
|
writes and patches revalidate the pinned parent identity; search and resource discovery descend
|
|
5673
5746
|
only through opened directories. A path that changed during authorization refuses with `CONFLICT`.
|
|
5674
|
-
|
|
5675
|
-
rule on `credentials`
|
|
5747
|
+
A path spelled differently from the name stored on disk refuses before any policy reads it, so on a
|
|
5748
|
+
case-insensitive volume a rule on `credentials` cannot be passed by asking for `CREDENTIALS`.
|
|
5676
5749
|
NUL bytes are refused before either host policy sees a path.
|
|
5677
5750
|
Reads and edits open file candidates without blocking on special files, then require a regular
|
|
5678
5751
|
file. A writerless FIFO therefore refuses instead of occupying a filesystem worker. `write_file`
|
|
@@ -5777,7 +5850,10 @@ remains the signal-only compatibility surface and additionally waits for admissi
|
|
|
5777
5850
|
|
|
5778
5851
|
The in-memory store is a reference adapter and has process-local durability
|
|
5779
5852
|
only. Production applications normally call `createAgentRuntimeStore()` and
|
|
5780
|
-
provide one database transaction driver
|
|
5853
|
+
provide one database transaction driver. Its `transaction` owns the retry of a
|
|
5854
|
+
serialization conflict, wherever the database reports it: on a statement or on
|
|
5855
|
+
`COMMIT`, which some clients surface in a different shape. Pass
|
|
5856
|
+
`armCommitConflict` to `runAgentStoreConformance` to prove it:
|
|
5781
5857
|
|
|
5782
5858
|
```ts
|
|
5783
5859
|
const store = createAgentRuntimeStore({
|
|
@@ -6366,6 +6442,46 @@ canonical run and failed assistant atomically, removes the run from recovery
|
|
|
6366
6442
|
indexes, and publishes the resulting `run-state`. A run that moved since it was
|
|
6367
6443
|
read returns a conflict; `staleOwner: true` is mandatory evidence, not a default.
|
|
6368
6444
|
|
|
6445
|
+
The same evidence holds inside one process when a store failure outlives the
|
|
6446
|
+
run's own handling. A store call around execution that throws is retried for
|
|
6447
|
+
about half a second, then decided by where the run stood. Retries bound a call
|
|
6448
|
+
that fails, not one that hangs, so give the store its own timeouts:
|
|
6449
|
+
|
|
6450
|
+
| Failure | Outcome of `result` |
|
|
6451
|
+
|---|---|
|
|
6452
|
+
| reading or acquiring the run, retries spent, or a lost reply to the acquisition | resolves with reason `abandoned`, the cause on the operator's `run-terminal` |
|
|
6453
|
+
| the first assistant draft | resolves with reason `runtime_failure`, like a checkpoint later in the turn |
|
|
6454
|
+
| a conflict on a run another executor holds | rejects with the conflict; the record stays that executor's |
|
|
6455
|
+
| a conflict on a run still queued, behind another run | rejects with `AgentRunUnsettledError`; `recover()` or `resume` picks it up |
|
|
6456
|
+
| the terminal commit, or closing the run, retries spent | rejects with `AgentRunUnsettledError` |
|
|
6457
|
+
|
|
6458
|
+
`AgentRunUnsettledError.run` is the record as last read, and the store may be ahead
|
|
6459
|
+
of it. Nothing in this process executes that run, and later turns of its
|
|
6460
|
+
conversation wait behind it. `recover()` skips a `running` run with a live owner, so
|
|
6461
|
+
a long-running process closes it itself once the store answers. Read the record
|
|
6462
|
+
first: with no owner or with `error.ownerId` it is this runtime's, and any other
|
|
6463
|
+
owner is executing it.
|
|
6464
|
+
|
|
6465
|
+
```ts
|
|
6466
|
+
const settled = await ticket.result.catch(async (error) => {
|
|
6467
|
+
if (!(error instanceof AgentRunUnsettledError)) throw error
|
|
6468
|
+
const { conversationId, id: runId } = error.run
|
|
6469
|
+
const current = await store.loadRun({ conversationId, runId })
|
|
6470
|
+
if (current && [undefined, error.ownerId].includes(current.run.ownerId)) {
|
|
6471
|
+
await runtime.abandon({
|
|
6472
|
+
conversationId,
|
|
6473
|
+
runId,
|
|
6474
|
+
expectedRevision: current.run.revision,
|
|
6475
|
+
staleOwner: true,
|
|
6476
|
+
})
|
|
6477
|
+
}
|
|
6478
|
+
throw error
|
|
6479
|
+
})
|
|
6480
|
+
```
|
|
6481
|
+
|
|
6482
|
+
A `queued` run lost to a run ahead of it, and `runtime.resume()` can run it instead
|
|
6483
|
+
once that one ends.
|
|
6484
|
+
|
|
6369
6485
|
## Store operations
|
|
6370
6486
|
|
|
6371
6487
|
`AgentRuntimeStore` remains the runtime-facing aggregate. Application adapters
|
|
@@ -9349,6 +9465,23 @@ migration.
|
|
|
9349
9465
|
literal in the type; division and rational shares return an explicit remainder. It does not own
|
|
9350
9466
|
exchange rates.
|
|
9351
9467
|
|
|
9468
|
+
`moneyFromDecimal('RUB', '5041.82', 2)` and `moneyToDecimal(value, 2)` cross between major units and
|
|
9469
|
+
minor ones with the currency's scale as a parameter, and a value finer than that scale is refused
|
|
9470
|
+
rather than cut. `multiplyMoneyByDecimal(price, '2.5', policy)` computes the exact product and rounds it
|
|
9471
|
+
once. `roundMoney(value, policy)` brings an amount to a table's steps:
|
|
9472
|
+
|
|
9473
|
+
```ts
|
|
9474
|
+
const shelfPrice = defineRoundingPolicy({
|
|
9475
|
+
mode: 'half-up', // or 'half-even', 'up', 'down'
|
|
9476
|
+
tiers: [{ below: '100000', step: '100' }, { step: '1000' }], // minor units
|
|
9477
|
+
})
|
|
9478
|
+
roundMoney(moneyFromDecimal('RUB', '5041.82', 2), shelfPrice) // { minor: '504000', currency: 'RUB' }
|
|
9479
|
+
```
|
|
9480
|
+
|
|
9481
|
+
A policy is data, so it can be stored with the prices it produced. `roundDecimal(value, policy)` is
|
|
9482
|
+
the same table over a plain decimal. `DecimalStringSchema` accepts one canonical spelling and refuses a
|
|
9483
|
+
JSON number; `DecimalInputSchema` takes a spelling such as a database's `10.50` to the canonical one.
|
|
9484
|
+
|
|
9352
9485
|
`defineUnitSystem` stores exact canonical decimal strings. Every conversion is an application
|
|
9353
9486
|
rational with an id. A converted `QuantityProjection` is `derived` and carries both its source and
|
|
9354
9487
|
conversion id; a form/database value can be marked `recorded`. Whether a derived projection is
|
|
@@ -10483,7 +10616,9 @@ myapp item_list --sort messages --top 5 --json # the same five, a
|
|
|
10483
10616
|
myapp item_list --sort name --ascending # ordered the other way
|
|
10484
10617
|
```
|
|
10485
10618
|
|
|
10486
|
-
Groups come back largest first for the same reason
|
|
10619
|
+
Groups come back largest first for the same reason, in the printed bytes even when a key looks
|
|
10620
|
+
like a number; a reader that parses them into a JavaScript object reorders such keys itself.
|
|
10621
|
+
A string equal to `null` or `(absent)` is quoted, so it stays apart from the missing value. A record that carries no
|
|
10487
10622
|
value for the sort field sorts **last in both directions**: it is not the
|
|
10488
10623
|
smallest, it is not on the scale at all, and letting it lead an ascending list
|
|
10489
10624
|
would answer a question nobody asked.
|
|
@@ -15916,19 +16051,46 @@ cast, and a send cancelled in the queue then waits for its slot and fails as
|
|
|
15916
16051
|
a follow-up call to action after an album costs one more. Reads and edits
|
|
15917
16052
|
(`getMe`, `getFile`, `editMessage*`, `deleteMessage`, `sendChatAction`) do not
|
|
15918
16053
|
spend the budget.
|
|
16054
|
+
- **Two lanes share one rate.** A call is `interactive` by default; a campaign passes
|
|
16055
|
+
`{ lane: 'bulk' }` to `run` or `acquire`, or installs
|
|
16056
|
+
`telegramBudgetTransformer(budget, { lane: 'bulk' })` on its own `Api`. A waiting
|
|
16057
|
+
interactive call is granted first, so a reply never queues behind recipients;
|
|
16058
|
+
the wait it can still see is the interval of the grant before it, up to one
|
|
16059
|
+
album. Bulk runs at whatever rate replies leave, and the sum never exceeds
|
|
16060
|
+
`messagesPerSecond`.
|
|
15919
16061
|
- **The first send after idle is immediate; the rest are spaced.** This is a
|
|
15920
16062
|
strict budget, not a bucket that saves a burst: at 24/s the next send is due
|
|
15921
|
-
one interval later.
|
|
15922
|
-
|
|
15923
|
-
|
|
15924
|
-
|
|
15925
|
-
|
|
15926
|
-
|
|
16063
|
+
one interval later. When a pause ends, one send is released at the deadline and
|
|
16064
|
+
the queue continues one interval at a time, never as a stampede.
|
|
16065
|
+
- **A 429 holds the chat that got it.** Telegram words a group's limit, a chat's
|
|
16066
|
+
pace and the bot's own limit alike, and only the addressee differs, so `run`
|
|
16067
|
+
pauses the `chat_id` of the refused send and every other chat of the token goes
|
|
16068
|
+
on at the shared rate. A person's reply does not wait behind a notification
|
|
16069
|
+
group on its limit, and neither does a campaign. Refusals from
|
|
16070
|
+
`tokenPauseChats` different chats (default 3) inside `tokenPauseWindowMs`
|
|
16071
|
+
(default 5000) are read as the bot's own limit and pause the whole token, and so
|
|
16072
|
+
does a 429 with no chat in the payload. `stats().pausedChats` counts the chats
|
|
16073
|
+
on a pause; `stats().pausedForMs` is the token's.
|
|
16074
|
+
- **A chat known to hit its limit can keep a 429 to itself.** A notification group
|
|
16075
|
+
runs into its per-minute limit as a matter of course. `{ refusal: 'call' }` on `run`,
|
|
16076
|
+
or `telegramBudgetTransformer(budget, { refusal: 'call' })` on that group's own `Api`,
|
|
16077
|
+
makes the refusal concern that send alone: nothing pauses and nothing counts towards
|
|
16078
|
+
`tokenPauseChats`. The call still spends the budget and waits out other pauses, and
|
|
16079
|
+
what to do with the refused message stays the caller's decision.
|
|
16080
|
+
- **A wait can be bounded.** A burst to one chat on pause otherwise queues without
|
|
16081
|
+
limit. `{ maxWaitMs }` on `run` or `acquire`, or on
|
|
16082
|
+
`telegramBudgetTransformer(budget, { maxWaitMs })`, ends a call that waited that
|
|
16083
|
+
long with `TelegramNotDispatchedError`, stage `queue`: nothing was sent and no
|
|
16084
|
+
message was spent, so repeating it is safe.
|
|
16085
|
+
- **It paces; it does not retry.** `run` calls the send once. A 429 pauses for
|
|
16086
|
+
Telegram's `retry_after` (or `retryAfterFallbackMs` when Telegram gave none) and
|
|
16087
|
+
rethrows; the caller's retry policy decides what happens next.
|
|
15927
16088
|
The refusal may be thrown or returned: a client transformer sits below the
|
|
15928
16089
|
place where grammY turns Telegram's `{ ok: false, error_code: 429, … }` body into
|
|
15929
16090
|
a `GrammyError`, so `run` reads that body too and hands it back unchanged.
|
|
15930
|
-
A 429 from an edit or a read
|
|
15931
|
-
|
|
16091
|
+
A 429 from an edit or a read concerns that call and pauses nothing;
|
|
16092
|
+
`budget.pauseAfter(error, { chatId })` remains for a caller who decides otherwise, and pauses
|
|
16093
|
+
the token when it names no chat. A network failure is rethrown unchanged and stays `unknown`; a 5xx
|
|
15932
16094
|
is rethrown unchanged and stays `server-error`. Neither is retried by the
|
|
15933
16095
|
budget or pauses the token — repeating a send whose outcome is unknown is a
|
|
15934
16096
|
decision, not a primitive's silent guess.
|
|
@@ -15940,9 +16102,9 @@ cast, and a send cancelled in the queue then waits for its slot and fails as
|
|
|
15940
16102
|
the client's own error — not evidence that Telegram did not accept it — so
|
|
15941
16103
|
keep the outcome unknown rather than retrying it as a definite failure.
|
|
15942
16104
|
- **Non-send retries wait without spending.** An edit or delete retry that got
|
|
15943
|
-
a 429
|
|
15944
|
-
|
|
15945
|
-
the token's
|
|
16105
|
+
a 429 records the pause with `budget.pauseAfter(error, { chatId })`, then uses
|
|
16106
|
+
`await budget.waitUntilResumed(signal, { chatId })`; it shares that chat's pause
|
|
16107
|
+
and the token's, and takes no message slot. Without `chatId` it pauses the token.
|
|
15946
16108
|
|
|
15947
16109
|
`telegramBroadcastSender` and `telegramOperatorSender` still send through
|
|
15948
16110
|
`callTelegramBotApi`; they are not a second budget. Their returned function does
|
|
@@ -16780,12 +16942,171 @@ Download schedules, licenses, database paths, attribution persistence and
|
|
|
16780
16942
|
analytics policy belong to the application.
|
|
16781
16943
|
|
|
16782
16944
|
|
|
16945
|
+
==============================================================================
|
|
16946
|
+
# Guide: XLSX (docs/guide/xlsx.md)
|
|
16947
|
+
==============================================================================
|
|
16948
|
+
|
|
16949
|
+
# XLSX
|
|
16950
|
+
|
|
16951
|
+
`stitchkit/xlsx` turns rows into a workbook. It is evolving, it needs nothing beyond the `zod` the package
|
|
16952
|
+
already requires, and it runs wherever
|
|
16953
|
+
`CompressionStream` does: Bun, Node and a browser.
|
|
16954
|
+
|
|
16955
|
+
```ts
|
|
16956
|
+
import { defineMoney } from 'stitchkit/primitives'
|
|
16957
|
+
import { renderXlsx, XLSX_MEDIA_TYPE } from 'stitchkit/xlsx'
|
|
16958
|
+
|
|
16959
|
+
const rub = defineMoney('RUB')
|
|
16960
|
+
|
|
16961
|
+
const bytes = await renderXlsx({
|
|
16962
|
+
sheets: [
|
|
16963
|
+
{
|
|
16964
|
+
name: 'Orders',
|
|
16965
|
+
columns: [
|
|
16966
|
+
{ key: 'title', header: 'Title', type: 'text' },
|
|
16967
|
+
{ key: 'count', header: 'Count', type: 'number', format: '0.00' },
|
|
16968
|
+
{ key: 'day', header: 'Day', type: 'date' },
|
|
16969
|
+
{ key: 'at', header: 'Created', type: 'datetime', timeZone: 'Asia/Bangkok' },
|
|
16970
|
+
{ key: 'total', header: 'Total', type: 'money', currency: 'RUB', scale: 2 },
|
|
16971
|
+
{ key: 'paid', header: 'Paid', type: 'boolean' },
|
|
16972
|
+
],
|
|
16973
|
+
rows: [
|
|
16974
|
+
{
|
|
16975
|
+
title: 'First order',
|
|
16976
|
+
count: '2.5',
|
|
16977
|
+
day: '2026-11-03',
|
|
16978
|
+
at: new Date(),
|
|
16979
|
+
total: rub.create('504182'),
|
|
16980
|
+
paid: true,
|
|
16981
|
+
},
|
|
16982
|
+
],
|
|
16983
|
+
},
|
|
16984
|
+
],
|
|
16985
|
+
})
|
|
16986
|
+
|
|
16987
|
+
return new Response(bytes, {
|
|
16988
|
+
headers: {
|
|
16989
|
+
'content-type': XLSX_MEDIA_TYPE,
|
|
16990
|
+
'content-disposition': `attachment; filename*=UTF-8''${encodeURIComponent('Übersicht.xlsx')}`,
|
|
16991
|
+
},
|
|
16992
|
+
})
|
|
16993
|
+
```
|
|
16994
|
+
|
|
16995
|
+
A missing or `null` value leaves the cell empty. The first row holds the headers, stays frozen and carries a
|
|
16996
|
+
filter. The same input produces the same bytes on one runtime; Bun and Node compress differently.
|
|
16997
|
+
|
|
16998
|
+
## Column types
|
|
16999
|
+
|
|
17000
|
+
| `type` | Cell value | In the file |
|
|
17001
|
+
|---|---|---|
|
|
17002
|
+
| `text` | string | text, whatever it looks like: `007` keeps its zeros |
|
|
17003
|
+
| `number` | number, bigint or decimal string | a number, with an optional Excel `format` |
|
|
17004
|
+
| `date` | `YYYY-MM-DD` | a date serial; a calendar date has no time zone to get wrong |
|
|
17005
|
+
| `datetime` | `Date` or ISO instant with an offset | the wall clock of the column's `timeZone` to the second, since a cell stores no zone |
|
|
17006
|
+
| `money` | `Money` in the column's `currency` | a number with `scale` places and the currency in its format |
|
|
17007
|
+
| `boolean` | boolean | a boolean |
|
|
17008
|
+
|
|
17009
|
+
## What it refuses
|
|
17010
|
+
|
|
17011
|
+
A value of the wrong type throws `XlsxCellError` naming the cell, such as `sheet "Orders" B2: expected a
|
|
17012
|
+
number or a decimal string`, instead of landing as a wrong cell.
|
|
17013
|
+
|
|
17014
|
+
Excel keeps numbers as doubles, so 15 significant digits are what survives. A decimal string or `Money` with
|
|
17015
|
+
more refuses rather than turning into a different amount. Keep such a value in a `text` column if it must
|
|
17016
|
+
stay exact and needs no arithmetic. Dates before 1900-03-01 refuse too: Excel counts a leap day that never
|
|
17017
|
+
happened, so its serials before it are off by one.
|
|
17018
|
+
|
|
17019
|
+
A wall clock cannot tell the two hours of a daylight-saving change apart: 05:30Z and 06:30Z on a New York
|
|
17020
|
+
autumn night are the same cell. Use `UTC` for a column whose order must survive that hour.
|
|
17021
|
+
|
|
17022
|
+
## Text is never a formula
|
|
17023
|
+
|
|
17024
|
+
Cells are written as inline strings, so `=HYPERLINK("x")` from a user's input is text a reader shows,
|
|
17025
|
+
and the file has no formula element at all. A cell that starts with `=`, `+`, `-` or `@` also carries Excel's
|
|
17026
|
+
quote prefix, so it stays text when a person edits that cell.
|
|
17027
|
+
|
|
17028
|
+
## Limits
|
|
17029
|
+
|
|
17030
|
+
Everything is built in memory, up to Excel's own 1 048 576 rows per sheet. There is no reading, no
|
|
17031
|
+
charts, no merged cells and no streaming: an application that needs them takes a library.
|
|
17032
|
+
|
|
17033
|
+
|
|
16783
17034
|
==============================================================================
|
|
16784
17035
|
# Guide: Upgrading (docs/guide/upgrading.md)
|
|
16785
17036
|
==============================================================================
|
|
16786
17037
|
|
|
16787
17038
|
# Upgrading stitchkit
|
|
16788
17039
|
|
|
17040
|
+
## Released migration: 0.117.0
|
|
17041
|
+
|
|
17042
|
+
### telegram-chat-pause
|
|
17043
|
+
|
|
17044
|
+
**Who must act:** code that relied on a 429 to one chat to stop the token's other chats. A bot that
|
|
17045
|
+
only sends needs no change: its other chats stop waiting.
|
|
17046
|
+
|
|
17047
|
+
`run` pauses the `chat_id` of a refused send; the token pauses when `tokenPauseChats` chats are refused
|
|
17048
|
+
inside `tokenPauseWindowMs`, or when the call has no chat
|
|
17049
|
+
([ADR 0278](../decisions/0278-a-telegram-429-holds-the-chat-that-got-it.md)). A second budget kept under a
|
|
17050
|
+
derived key to isolate one group is no longer needed: remove it. Where one refusal must still stop everything,
|
|
17051
|
+
say so:
|
|
17052
|
+
|
|
17053
|
+
```ts
|
|
17054
|
+
// before
|
|
17055
|
+
await api.sendMessage(chatId, text)
|
|
17056
|
+
// after
|
|
17057
|
+
await api.sendMessage(chatId, text).catch((error) => {
|
|
17058
|
+
budget.pauseAfter(error)
|
|
17059
|
+
throw error
|
|
17060
|
+
})
|
|
17061
|
+
```
|
|
17062
|
+
|
|
17063
|
+
The catch belongs at the `Api` call, where grammY has turned a returned refusal into a thrown one. A raw
|
|
17064
|
+
`budget.run` around a client transformer's `send` gets that refusal as a value.
|
|
17065
|
+
|
|
17066
|
+
Search pattern: `budget.run`, and budgets created under a key derived from the token.
|
|
17067
|
+
|
|
17068
|
+
### tracking-contract-entry
|
|
17069
|
+
|
|
17070
|
+
**Who must act:** code that imports `createTrackingContract` or a tracking schema or wire type from
|
|
17071
|
+
`stitchkit/tracking`. Code that only uses the browser client needs no change.
|
|
17072
|
+
|
|
17073
|
+
The wire shapes moved to `stitchkit/tracking/contract`, so a page that sends events no longer carries `zod`
|
|
17074
|
+
([ADR 0280](../decisions/0280-real-visitor-speed-is-collected-after-the-page.md)):
|
|
17075
|
+
|
|
17076
|
+
```ts
|
|
17077
|
+
// before
|
|
17078
|
+
import { createTrackingContract, createTrackingSchemas } from 'stitchkit/tracking'
|
|
17079
|
+
// after
|
|
17080
|
+
import { createTrackingContract, createTrackingSchemas } from 'stitchkit/tracking/contract'
|
|
17081
|
+
```
|
|
17082
|
+
|
|
17083
|
+
Search pattern: imports from `stitchkit/tracking` naming `createTrackingContract`, `createTrackingSchemas`,
|
|
17084
|
+
`UtmData`, `AttributionData`, `TrackingEventEnvelope`, `TrackingDisposition`, `TrackEvents*`, `VisitEntryContext`,
|
|
17085
|
+
`VisitBootstrapResponse`, `TrackingOutboxState` or `DEFAULT_BUILD_ID_PATTERN`.
|
|
17086
|
+
|
|
17087
|
+
## Released migration: 0.116.0
|
|
17088
|
+
|
|
17089
|
+
### send-only-telegram-pause
|
|
17090
|
+
|
|
17091
|
+
**Who must act:** code that relied on a 429 from `editMessage*`, `deleteMessage` or a read to stop a
|
|
17092
|
+
token's sends. Code that only sends needs no change.
|
|
17093
|
+
|
|
17094
|
+
`run` pauses the token only on a method that spends the budget
|
|
17095
|
+
([ADR 0277](../decisions/0277-a-telegram-budget-serves-replies-first.md)). Where an edit must still stop
|
|
17096
|
+
the sends, say so:
|
|
17097
|
+
|
|
17098
|
+
```ts
|
|
17099
|
+
// before
|
|
17100
|
+
await budget.run('editMessageText', payload, signal, send)
|
|
17101
|
+
// after
|
|
17102
|
+
await budget.run('editMessageText', payload, signal, send).catch((error) => {
|
|
17103
|
+
budget.pauseAfter(error)
|
|
17104
|
+
throw error
|
|
17105
|
+
})
|
|
17106
|
+
```
|
|
17107
|
+
|
|
17108
|
+
Search pattern: `budget.run` next to edit and read methods.
|
|
17109
|
+
|
|
16789
17110
|
## Released migration: 0.115.0
|
|
16790
17111
|
|
|
16791
17112
|
### contained-cli-downloads
|
|
@@ -22135,6 +22456,10 @@ schedules, transports and file generation remain application-owned.
|
|
|
22135
22456
|
| `PermissionGrantMatrix` / `PermissionCheckResult` | _types_ | compile-time complete matrix and unknown/allowed/denied runtime result |
|
|
22136
22457
|
| `defineMoney` / `createMoneySchema` | functions | fixed-currency JSON-safe minor-unit value and Zod schema |
|
|
22137
22458
|
| `addMoney` / `subtractMoney` / `multiplyMoney` / `shareMoney` / `splitMoney` | functions | exact same-currency arithmetic with explicit indivisible remainder |
|
|
22459
|
+
| `moneyFromDecimal` / `moneyToDecimal` | functions | major units to minor ones and back, with the currency's scale as a parameter |
|
|
22460
|
+
| `multiplyMoneyByDecimal` / `roundMoney` | functions | an exact product rounded once, and an amount brought to a policy's steps |
|
|
22461
|
+
| `defineRoundingPolicy` / `roundDecimal` / `RoundingPolicySchema` / `RoundingPolicy` / `RoundingModeSchema` / `RoundingMode` | functions, schemas, _types_ | a rounding table as data, by magnitude, and its modes |
|
|
22462
|
+
| `DecimalStringSchema` / `DecimalInputSchema` | schemas | one canonical decimal spelling, and the input form that reaches it; a number is refused |
|
|
22138
22463
|
| `Money` / `MoneyShare` / `MoneySplit` | _types_ | currency-literal value and remainder-bearing operation results |
|
|
22139
22464
|
| `defineUnitSystem` / `createQuantitySchema` / `addQuantity` | functions | exact decimal quantities and caller-declared finite conversions |
|
|
22140
22465
|
| `Quantity` / `UnitConversion` / `QuantityProjection` | _types_ | unit-literal value, rational conversion and recorded/derived provenance |
|
|
@@ -22833,6 +23158,7 @@ Server-only optional application runtime. See the
|
|
|
22833
23158
|
| `AgentRuntimeRecoverOptions` | _type_ | bounded paged startup recovery with causal per-conversation scheduling, context resolver and explicit evidence policy |
|
|
22834
23159
|
| `AgentRuntimeAbandonInput` | _type_ | one known orphan identity, expected revision and mandatory stale-owner evidence for `runtime.abandon()` |
|
|
22835
23160
|
| `AgentRuntimeConflictError` | class | thrown when a store mutation loses to a concurrent writer — catchable by type from `stitchkit/agent-runtime` |
|
|
23161
|
+
| `AgentRunUnsettledError` | class | rejects `result` when the store never answered: `.run` is still active with no executor, close it with `runtime.abandon()` |
|
|
22836
23162
|
| `AgentSessionCloseOptions` | _type_ | `gracePeriodMs` for natural settlement, then abort, then `forceTimeoutMs` for bounded settlement after it |
|
|
22837
23163
|
| `AgentSessionCloseResult` | _type_ | what `close()` achieved: `settled`, or `timedOut` with `remaining` runs still in flight. Only omitting `forceTimeoutMs` guarantees nothing is in flight on return |
|
|
22838
23164
|
| `AgentHistoryProjectionOptions` | _type_ | storage-neutral file resolver, explicit unresolved-file behavior, and how an interrupted turn reaches the model (`interruptedAssistant`) |
|
|
@@ -23828,7 +24154,7 @@ injected grammY bot. → ADR 0143, ADR 0201, ADR 0274 — [guide](../guide/teleg
|
|
|
23828
24154
|
| `TelegramFetch` / `TelegramBotTransportOpen` / `TelegramBotTransportResolve` / `TelegramBotTransportSocket` | _types_ | the `(url, init) => Promise<Response>` the senders take; the transport's socket factory, resolver and the structural socket it needs (a Node `net.Socket` or `tls.TLSSocket` fits) |
|
|
23829
24155
|
| `TelegramNotDispatchedError` / `TelegramNotDispatchedStage` | class / _type_ | a request that provably never left (`lookup`, `connect` or `request`), so repeating it cannot duplicate a message |
|
|
23830
24156
|
| `TelegramSendFailureReason` / `TELEGRAM_SEND_FAILURE_REASONS` | _type_ / constant | `blocked-by-user` / `user-deactivated` / `chat-not-found` / `not-started` / `bot-to-bot-disabled` / `rate-limited` / `message-invalid` / `server-error` / `not-dispatched` / `unknown`, and the same as a list; a `Record<TelegramSendFailureReason, …>` over them fails to compile when a reason is added |
|
|
23831
|
-
| `telegramOutboundBudget` / `TelegramOutboundBudget` / `TelegramOutboundBudgetConfig` / `TelegramOutboundBudgetStats` | function / _type_ | one token-wide outbound budget: one object per token in the process, strict 24 messages/s by default, weighted albums and multi-message copies, `run(method, payload, signal, send)` as the transformer adapter (a 429 pauses
|
|
24157
|
+
| `telegramOutboundBudget` / `TelegramOutboundBudget` / `TelegramOutboundBudgetConfig` / `TelegramOutboundBudgetStats` / `TelegramOutboundCallOptions` / `TelegramOutboundChatOptions` / `TelegramOutboundLane` | function / _type_ | one token-wide outbound budget: one object per token in the process, strict 24 messages/s by default, weighted albums and multi-message copies, an `interactive` lane granted before `bulk`, `run(method, payload, signal, send, { lane, maxWaitMs })` as the transformer adapter (a 429 on a send pauses its `chat_id` whether `send` throws it or resolves with the Bot API's `{ ok: false, … }` body, and refusals from `tokenPauseChats` chats pause the token), `pause`/`pauseAfter` for 429, `waitUntilResumed` for non-send retries and `stats`; it paces and never retries, and no public value carries the token |
|
|
23832
24158
|
| `runTelegramBroadcast` | function | a resumable broadcast by `name` under a state `directory`: audience and journal durably recorded, pacing at `ratePerSecond` (25), exact retry delays within the finite declared budget, injected classifier, uncertain send outcomes never replayed, `dryRun`, one fenced runner per name |
|
|
23833
24159
|
| `TelegramBroadcastConfig` / `TelegramBroadcastSend` / `TelegramBroadcastReport` / `TelegramBroadcastRunOutcome` / `TelegramBroadcastOutcome` / `TelegramBroadcastRecipient` | _type_ | `name`, `directory`, `recipients()`, `send({ recipient, attempt })`, `maxAttempts`, `signal`, `onProgress`; counts `delivered` / `unreachable` / `failed` / `uncertain` / `pending` and `finished` / `stopped` / `halted` / `dry-run` |
|
|
23834
24160
|
| `TelegramBroadcastFailureSchema` / `TelegramBroadcastFailure` | schema / _type_ | injected `classify` answers retry-after/transient/permanent/ambiguous; exact provider delay is bounded by `maxRetryDelayMs` without shortening, unknown outcome remains uncertain and is never replayed |
|
|
@@ -23935,8 +24261,9 @@ the whole dependency. → ADR 0211 — [guide](../guide/telegram-screens.md)
|
|
|
23935
24261
|
|
|
23936
24262
|
Browser-safe visitor-tracking mechanics, with no event vocabulary of their own
|
|
23937
24263
|
and no React. The React wrapper (a provider and a hook, ~40 lines) is the
|
|
23938
|
-
application's — it owns the router and the session.
|
|
23939
|
-
`stitchkit/tracking/
|
|
24264
|
+
application's — it owns the router and the session. It imports no `zod`: the wire
|
|
24265
|
+
shapes are `stitchkit/tracking/contract`, the server half is
|
|
24266
|
+
`stitchkit/tracking/server`. → ADR 0166, ADR 0280 — [guide](../guide/tracking.md)
|
|
23940
24267
|
|
|
23941
24268
|
| Export | Kind | Summary |
|
|
23942
24269
|
|--------|------|---------|
|
|
@@ -23945,15 +24272,6 @@ application's — it owns the router and the session. The server half is
|
|
|
23945
24272
|
| `TrackFn` / `EventsWithMetadata` / `EventsWithoutMetadata` | _type_ | `track(type)` / `track(type, metadata)` typed by the application's metadata map, and the two halves of that map |
|
|
23946
24273
|
| `BuiltinTrackingEventTypes` / `CONVENTIONAL_TRACKING_EVENT_TYPES` | _type_ / const | the application's names for the events the client emits itself; the conventional `PAGE_VIEW` … `INTERACTION` set |
|
|
23947
24274
|
| `browserTrackingHost` / `TrackingHost` / `TrackingPageContext` | function / _type_ | everything the client takes from a browser tab behind one interface — page context, visibility, scroll depth, events, intervals, clocks, storage — so a test or a non-tab host can supply its own |
|
|
23948
|
-
| `createTrackingContract` / `TrackingContractConfig` / `TrackingContractEndpoints` | function / _type_ | `bootstrap` (`POST /visit`) and `track` (`POST /events`, `safelistedBody`) from the application's event types, scope and optional extras |
|
|
23949
|
-
| `createTrackingSchemas` / `TrackingSchemas` / `TrackingSchemasConfig` | function / _type_ | every schema of one surface — `event`, `request`, `response`, `disposition`, `entry`, `bootstrap` — from `eventTypes`, optional `eventExtras`, `buildIdPattern`, `maxEventsPerBatch` |
|
|
23950
|
-
| `TrackingEventEnvelope` / `TrackingEventShape` | _type_ | `eventId`, `visitId`, `browserStreamId`, `browserSequence`, `type`, `page`, `metadata`, `clientTimestamp` |
|
|
23951
|
-
| `TrackEventsRequest` / `TrackEventsResponse` / `TrackEventsResponseSchema` | _type_ / schema | a batch (`buildId`, `events`, `utm`) and its answer (`accepted`, `dispositions`) |
|
|
23952
|
-
| `TrackingDisposition` / `TrackingDispositionSchema` / `TrackingDispositionStatus` / `TrackingDispositionStatusSchema` | _type_ / schema | `accepted` / `duplicate` / `identity-invalid` / `excluded-bot` — every one terminal |
|
|
23953
|
-
| `VisitEntryContext` / `VisitEntryContextShape` / `VisitBootstrapResponse` / `VisitBootstrapResponseSchema` | _type_ / schema | what a browser reports to open a visit (lineage, landing, referrer, UTM, display, screen, `buildId`, outbox health) and the lease it gets back |
|
|
23954
|
-
| `TrackingOutboxState` / `TrackingOutboxStateSchema` | _type_ / schema | `available` / `unavailable` |
|
|
23955
|
-
| `UtmData` / `UtmDataSchema` / `AttributionData` / `AttributionDataSchema` | _type_ / schema | campaign tags and a touch (UTM, referrer, landing page) |
|
|
23956
|
-
| `DEFAULT_BUILD_ID_PATTERN` | const | `dev` or a git SHA |
|
|
23957
24275
|
| `createTrackingOutbox` / `TrackingOutbox` / `TrackingOutboxOptions` / `TrackingOutboxHealth` | function / _type_ | the tab-shared outbox: `streamId`, `reserveSequences`, `enqueue`, `acquireLease` / `releaseLease` (short, released on leave), `readBatch`, `acknowledge`, `health`; bounded by count and age |
|
|
23958
24276
|
| `TrackingOutboxStorage` / `TrackingOutboxMeta` / `TrackingOutboxEvents` / `TrackingOutboxRecord` / `TrackingQueuedEvent` | _type_ | one atomic, all-or-nothing `transact` over a key/value store and an event store (`readonly` for pure reads) — the contract an adapter implements |
|
|
23959
24277
|
| `indexedDbOutboxStorage` / `memoryOutboxStorage` | function | the IndexedDB adapter (one `readwrite` transaction per `transact`) and the in-memory one for tests and degraded mode |
|
|
@@ -23965,10 +24283,38 @@ application's — it owns the router and the session. The server half is
|
|
|
23965
24283
|
| `createOncePerPage` / `OncePerPage` | function / _type_ | one fact per key per page within a window — the guard a twice-mounted component cannot keep itself |
|
|
23966
24284
|
| `resolveAttribution` / `ResolveAttributionInput` / `ResolvedAttribution` / `AttributionStorage` / `ReferrerRule` | function / _type_ | first-touch once, current-touch on every new UTM; the referrer → source/medium map is the application's, there is no built-in list |
|
|
23967
24285
|
| `parseUtmFromSearch` / `parseReferrer` | function | the two parsers `resolveAttribution` is built from |
|
|
24286
|
+
| `redactTrackingText` / `redactTrackingValue` | functions | mask passwords, sign-in credentials and signed launch data in text or a value of any shape; the key stays, and navigation and campaign parameters are untouched |
|
|
23968
24287
|
| `resolveTrackedClick` / `ResolveTrackedClickOptions` / `TrackedClick` / `TrackedClickAttributes` / `ClickTarget` | function / _type_ | `data-track` → click, `data-track-action` → interaction, a link to another origin → outbound; attribute names configurable; `hrefPolicy: 'full'` retains query/fragment (default `path-only` omits them), URL credentials excluded; `leavesPage` says which path to send on |
|
|
23969
24288
|
|
|
23970
24289
|
---
|
|
23971
24290
|
|
|
24291
|
+
## `stitchkit/tracking/contract`
|
|
24292
|
+
|
|
24293
|
+
The wire shapes of tracking, for a server and a shared contract. It brings `zod`, so
|
|
24294
|
+
browser code that only sends events does not import it. → ADR 0280
|
|
24295
|
+
|
|
24296
|
+
| Export | Kind | Summary |
|
|
24297
|
+
|--------|------|---------|
|
|
24298
|
+
| `createTrackingContract` / `TrackingContractConfig` / `TrackingContractEndpoints` | function / _type_ | `bootstrap` (`POST /visit`) and `track` (`POST /events`, `safelistedBody`) from the application's event types, scope and optional extras |
|
|
24299
|
+
| `createTrackingSchemas` / `TrackingSchemas` / `TrackingSchemasConfig` | function / _type_ | every schema of one surface — `event`, `request`, `response`, `disposition`, `entry`, `bootstrap` — from `eventTypes`, optional `eventExtras`, `buildIdPattern`, `maxEventsPerBatch` |
|
|
24300
|
+
| `TrackingEventEnvelope` / `TrackingEventShape` | _type_ | `eventId`, `visitId`, `browserStreamId`, `browserSequence`, `type`, `page`, `metadata`, `clientTimestamp` |
|
|
24301
|
+
| `TrackEventsRequest` / `TrackEventsResponse` / `TrackEventsResponseSchema` | _type_ / schema | a batch (`buildId`, `events`, `utm`) and its answer (`accepted`, `dispositions`) |
|
|
24302
|
+
| `TrackingDisposition` / `TrackingDispositionSchema` / `TrackingDispositionStatus` / `TrackingDispositionStatusSchema` | _type_ / schema | `accepted` / `duplicate` / `identity-invalid` / `excluded-bot` — every one terminal |
|
|
24303
|
+
| `VisitEntryContext` / `VisitEntryContextShape` / `VisitBootstrapResponse` / `VisitBootstrapResponseSchema` | _type_ / schema | what a browser reports to open a visit (lineage, landing, referrer, UTM, display, screen, `buildId`, outbox health) and the lease it gets back |
|
|
24304
|
+
| `TrackingOutboxState` / `TrackingOutboxStateSchema` | _type_ / schema | `available` / `unavailable` |
|
|
24305
|
+
| `UtmData` / `UtmDataSchema` / `AttributionData` / `AttributionDataSchema` | _type_ / schema | campaign tags and a touch (UTM, referrer, landing page) |
|
|
24306
|
+
| `DEFAULT_BUILD_ID_PATTERN` | const | `dev` or a git SHA |
|
|
24307
|
+
| `PerformanceSampleSchema` / `PerformanceSample` / `PerformanceVitalsSchema` / `PerformanceVitals` | schemas / _types_ | one page load's wire shape; a metric that did not arrive is `null` |
|
|
24308
|
+
| `summarizePerformanceMetric` / `PerformanceMetricSummary` | function / _type_ | sample count, p75, and a p95 only from 100 samples |
|
|
24309
|
+
|
|
24310
|
+
## `stitchkit/tracking/performance`
|
|
24311
|
+
|
|
24312
|
+
| Export | Kind | Summary |
|
|
24313
|
+
|--------|------|---------|
|
|
24314
|
+
| `createPerformanceCollector` / `PerformanceCollector` / `PerformanceCollectorConfig` | function / _types_ | Web Vitals of this page after load: an `early` sample and a `final` beacon to `url`, undelivered ones kept for the next page; inert for a lab agent |
|
|
24315
|
+
| `PerformanceHost` / `VitalsLibrary` / `VitalsMetric` / `VitalsObserver` | _types_ | the seams of the collector: the page, and the part of `web-vitals` it uses |
|
|
24316
|
+
| `isLabUserAgent` | function | whether an agent admits to being a lab run or a crawler |
|
|
24317
|
+
|
|
23972
24318
|
## `stitchkit/tracking/server`
|
|
23973
24319
|
|
|
23974
24320
|
The decisions a tracking backend makes, without the storage it makes them
|
|
@@ -23984,6 +24330,7 @@ algorithm over a store the application implements. → ADR 0166
|
|
|
23984
24330
|
| `activeIntervalOf` / `ActiveTimeInterval` / `ActiveIntervalOptions` | function / _type_ | the visible-time interval an event carries, dated by the server clock; `null` for an unshaped or implausible one |
|
|
23985
24331
|
| `hashTrackingEvent` | function | `sha256(JSON.stringify(parsedEvent))` — the stored payload hash, unsorted on purpose so it matches what applications already hold |
|
|
23986
24332
|
| `isBotUserAgent` / `DEFAULT_BOT_USER_AGENT_PATTERN` | function / const | crawlers, monitors and `headlesschrome` are not visitors |
|
|
24333
|
+
| `trackingTelemetryHealth` / `TrackingOutboxReport` / `TrackingTelemetryHealth` | function / _types_ | `ok`, `degraded` or `unknown` from outbox reports; a lifetime `dropped` counts only by what it grew over `baselines` |
|
|
23987
24334
|
| `createPresenceRegistry` / `PresenceRegistry` / `PresenceEntry` | function / _type_ | who is here now, in this process: `touch` / `snapshot(filter)` / `presentVisitOf` with a 45 s TTL |
|
|
23988
24335
|
|
|
23989
24336
|
---
|
|
@@ -24022,6 +24369,15 @@ providers, the microphone and the player remain application-owned. See the
|
|
|
24022
24369
|
|
|
24023
24370
|
---
|
|
24024
24371
|
|
|
24372
|
+
## `stitchkit/xlsx`
|
|
24373
|
+
|
|
24374
|
+
| Export | Kind | Summary |
|
|
24375
|
+
|--------|------|---------|
|
|
24376
|
+
| `renderXlsx` | function | a workbook from rows and typed columns as XLSX bytes; deterministic, no formula in the file |
|
|
24377
|
+
| `XLSX_MEDIA_TYPE` | constant | the media type for `defineExportOperation` and a download response |
|
|
24378
|
+
| `XlsxCellError` | class | a value of the wrong type, or one Excel cannot hold exactly, with the cell's address |
|
|
24379
|
+
| `XlsxWorkbookSchema` / `XlsxWorkbook` / `XlsxSheetSchema` / `XlsxColumnSchema` / `XlsxColumn` | schemas / _types_ | the workbook, its sheets and the six column types |
|
|
24380
|
+
|
|
24025
24381
|
## `stitchkit/geo`
|
|
24026
24382
|
|
|
24027
24383
|
Server-only managed GeoIP generations. Applications own database acquisition,
|