stitchkit 0.95.3 → 0.96.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 +99 -0
- package/dist/application/grammy-bot.d.ts +44 -0
- package/dist/application/grammy-bot.d.ts.map +1 -0
- package/dist/application/grammy-update-admission.d.ts +35 -0
- package/dist/application/grammy-update-admission.d.ts.map +1 -0
- package/dist/application/grammy.d.ts +23 -4
- package/dist/application/grammy.d.ts.map +1 -1
- package/dist/application/kernel-admission.d.ts +11 -0
- package/dist/application/kernel-admission.d.ts.map +1 -1
- package/dist/application/kernel-resource-context.d.ts.map +1 -1
- package/dist/application/resource.d.ts +22 -0
- package/dist/application/resource.d.ts.map +1 -1
- package/dist/entrypoints/application/grammy.d.ts +2 -1
- package/dist/entrypoints/application/grammy.d.ts.map +1 -1
- package/dist/entrypoints/application/grammy.js +139 -5
- package/dist/entrypoints/application.d.ts +1 -1
- package/dist/entrypoints/application.d.ts.map +1 -1
- package/dist/entrypoints/application.js +1 -1
- package/dist/entrypoints/observability.d.ts +1 -0
- package/dist/entrypoints/observability.d.ts.map +1 -1
- package/dist/entrypoints/observability.js +46 -4
- package/dist/entrypoints/telegram.d.ts +4 -0
- package/dist/entrypoints/telegram.d.ts.map +1 -1
- package/dist/entrypoints/telegram.js +680 -88
- package/dist/entrypoints/testing.js +1 -1
- package/dist/{index-w906953s.js → index-5qn1kx34.js} +48 -0
- package/dist/observability/json-logger.d.ts +38 -0
- package/dist/observability/json-logger.d.ts.map +1 -0
- package/dist/telegram/bot-api.d.ts +50 -0
- package/dist/telegram/bot-api.d.ts.map +1 -0
- package/dist/telegram/broadcast-state.d.ts +67 -0
- package/dist/telegram/broadcast-state.d.ts.map +1 -0
- package/dist/telegram/broadcast.d.ts +93 -0
- package/dist/telegram/broadcast.d.ts.map +1 -0
- package/dist/telegram/local-files.d.ts +63 -0
- package/dist/telegram/local-files.d.ts.map +1 -0
- package/dist/telegram/operator-channel.d.ts +72 -0
- package/dist/telegram/operator-channel.d.ts.map +1 -0
- package/llms/application-grammy.txt +5 -0
- package/llms/application.2.txt +1 -0
- package/llms/application.txt +51 -13
- package/llms/observability.txt +5 -0
- package/llms/server.4.txt +3 -0
- package/llms/stitchkit.txt +5 -5
- package/llms/telegram.txt +159 -3
- package/llms/{upgrading-0.37.0-0.53.0.txt → upgrading-0.37.0-0.56.0.txt} +130 -3
- package/llms/{upgrading-0.55.0-0.68.0.txt → upgrading-0.57.0-0.69.0.txt} +44 -130
- package/llms/{upgrading-0.69.0-0.95.0.txt → upgrading-0.70.0-0.96.0.txt} +40 -44
- package/llms/upgrading.txt +3 -3
- package/llms-full.txt +265 -23
- package/llms.txt +10 -9
- package/package.json +1 -1
package/llms-full.txt
CHANGED
|
@@ -60,7 +60,7 @@ own, recorded as an ADR.
|
|
|
60
60
|
| `stitchkit/cli` | server | stable | `createCli` — the CLI transport, light (no MCP SDK / `ai`) |
|
|
61
61
|
| `stitchkit/remote` | browser **and** server | stable | peer-free `implementRemote` for thin HTTP proxy processes |
|
|
62
62
|
| `stitchkit/files` | server (Bun or Node) | stable | peer-free managed local-file boundary |
|
|
63
|
-
| `stitchkit/telegram` | server (Bun or Node) | evolving | peer-free Telegram platform primitives — Mini App `initData` verification and Bot API
|
|
63
|
+
| `stitchkit/telegram` | server (Bun or Node) | evolving | peer-free Telegram platform primitives — Mini App `initData` verification, send-failure classification, resumable broadcasts, the operator channel and local Bot API files |
|
|
64
64
|
| `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 |
|
|
65
65
|
| `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 |
|
|
66
66
|
| `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 |
|
|
@@ -68,11 +68,11 @@ own, recorded as an ADR.
|
|
|
68
68
|
| `stitchkit/geo` | server (Bun or Node) | evolving | managed GeoIP reader generations, last-known-good reload and the optional MaxMind adapter |
|
|
69
69
|
| `stitchkit/oauth` | browser **and** server | evolving | provider-neutral Authorization Code + PKCE request and one-shot callback transaction mechanics |
|
|
70
70
|
| `stitchkit/google` | server (Bun or Node) | evolving | optional Google code exchange and verified OIDC identity adapter |
|
|
71
|
-
| `stitchkit/observability` | server | stable<br>_redefined in 2 of the
|
|
71
|
+
| `stitchkit/observability` | server | stable<br>_redefined in 2 of the 41 minors since 0.56.2, most recently 0.92.0_ | request/tool event projections — `createObservability`, trace context, sanitisation |
|
|
72
72
|
| `stitchkit/testing` | tests on Bun or Node | stable | in-process generated clients over a real Fetch handler, plus the managed-resource conformance kit |
|
|
73
73
|
| `stitchkit/declaration` | browser + build and deployment tooling (Bun or Node) | evolving | `ProjectDeclarationSchema` — the one machine-readable statement a repository makes about itself |
|
|
74
74
|
| `stitchkit/react` | browser + server rendering | stable | `createCursorQuery`, `createCacheBridge`, QueryClient and `ApiError` retry policy |
|
|
75
|
-
| `stitchkit/agent-runtime` | server | evolving<br>_redefined in 21 of the
|
|
75
|
+
| `stitchkit/agent-runtime` | server | evolving<br>_redefined in 21 of the 41 minors since 0.56.2, most recently 0.94.0_ | optional durable conversation/run loop, history, models, prompts, fencing and events |
|
|
76
76
|
| `stitchkit/agent-runtime/sandbox` | server | evolving | optional Linux Bubblewrap sessions, durable workspaces and a host HTTP credential gateway; see [sandbox guide](sandbox.md) |
|
|
77
77
|
| `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 |
|
|
78
78
|
| `stitchkit/agent-runtime/harness` | server | evolving | resource-aware process-local facade over the canonical Agent runtime; supervision stays outside |
|
|
@@ -82,8 +82,8 @@ own, recorded as an ADR.
|
|
|
82
82
|
| `stitchkit/agent-runtime/sqlite/bun` | server (Bun) | evolving | durable built-in SQLite store for the agent runtime |
|
|
83
83
|
| `stitchkit/agent-runtime/sqlite/node` | server (Node ≥ 22.5) | evolving | durable built-in SQLite store for the agent runtime |
|
|
84
84
|
| `stitchkit-tui` | terminal (Bun) | evolving | optional official OpenTUI host over a caller-composed headless runtime |
|
|
85
|
-
| `stitchkit/application` | browser + server | evolving<br>_redefined in
|
|
86
|
-
| `stitchkit/application/grammy` | server | evolving |
|
|
85
|
+
| `stitchkit/application` | browser + server | evolving<br>_redefined in 9 of the 41 minors since 0.56.2, most recently 0.96.0_ | managed resource graph, readiness, admission, schedules, subtree restart and bounded shutdown |
|
|
86
|
+
| `stitchkit/application/grammy` | server | evolving | a grammY bot as application resources — configuration, then polling with updates admitted by batch — and the webhook adapter |
|
|
87
87
|
| `stitchkit/application/opentelemetry` | server | evolving | maps application snapshots onto an injected OpenTelemetry `Meter` |
|
|
88
88
|
| `stitchkit/application/schemas` | browser + server | evolving | the application's snapshot, health and shutdown schemas alone, without the kernel |
|
|
89
89
|
| `stitchkit/application/diagnostic-journal` | server | evolving | the local diagnostic journal — the one part of the kernel that spawns, locks and writes files |
|
|
@@ -7003,40 +7003,78 @@ Install grammY only in an application that imports the adapter:
|
|
|
7003
7003
|
|
|
7004
7004
|
```ts
|
|
7005
7005
|
import { Bot } from 'grammy'
|
|
7006
|
-
import {
|
|
7007
|
-
createGrammyWebhookResource,
|
|
7008
|
-
grammyPollingResource,
|
|
7009
|
-
} from 'stitchkit/application/grammy'
|
|
7006
|
+
import { grammyBotResources } from 'stitchkit/application/grammy'
|
|
7010
7007
|
```
|
|
7011
7008
|
|
|
7012
|
-
|
|
7009
|
+
A long-polling bot is two resources, and one call gives both:
|
|
7013
7010
|
|
|
7014
7011
|
```ts
|
|
7015
7012
|
const bot = new Bot(env.BOT_TOKEN)
|
|
7016
7013
|
bot.command('start', (ctx) => ctx.reply('Hello'))
|
|
7017
|
-
bot.catch(({ error }) =>
|
|
7014
|
+
bot.catch(({ error }) => logger.error('Telegram handler failed', { error }))
|
|
7018
7015
|
|
|
7019
|
-
const telegram =
|
|
7020
|
-
id: 'telegram',
|
|
7016
|
+
const telegram = grammyBotResources({
|
|
7021
7017
|
bot,
|
|
7022
|
-
|
|
7023
|
-
})
|
|
7024
|
-
|
|
7025
|
-
|
|
7026
|
-
|
|
7027
|
-
|
|
7028
|
-
|
|
7018
|
+
dependsOn: [database, http], // what handlers need before the first update
|
|
7019
|
+
configure: () => bot.api.setMyCommands([{ command: 'start', description: 'Start' }]),
|
|
7020
|
+
onEnded: ({ error }) => stopProcess(error), // see below
|
|
7021
|
+
})
|
|
7022
|
+
|
|
7023
|
+
const app = createApplication({ id: 'bot', resources: [database, http, ...telegram.resources] })
|
|
7024
|
+
```
|
|
7025
|
+
|
|
7026
|
+
The ids are stable — `telegram-configuration`, then `telegram-polling` — so a
|
|
7027
|
+
monitor, a test or `app.restart()` can name them. `configure` runs on every
|
|
7028
|
+
start, before polling, so a command menu that failed to publish fails the start
|
|
7029
|
+
instead of hiding behind a bot that answers.
|
|
7030
|
+
|
|
7031
|
+
**Updates are admitted by batch.** grammY confirms a batch to Telegram by
|
|
7032
|
+
asking for the next one, and it asks as soon as the batch went through
|
|
7033
|
+
middleware — whatever the middleware did. An update that arrived while the
|
|
7034
|
+
application was starting, degraded or stopping was refused by its handler and
|
|
7035
|
+
then confirmed as handled. The polling resource now waits for admission before
|
|
7036
|
+
asking for a batch, holds one admission lease for the batch it returns to
|
|
7037
|
+
grammY, and releases it when grammY asks for the next. On shutdown the batch in
|
|
7038
|
+
hand is finished before `bot.stop()` confirms the offset, so a stop neither
|
|
7039
|
+
loses an update nor hands the next process one this process already handled; a
|
|
7040
|
+
batch that arrives once the application will not admit it is never returned, and
|
|
7041
|
+
Telegram keeps it for the next process.
|
|
7042
|
+
|
|
7043
|
+
**When polling ends on its own** — Telegram refused the token (401), another
|
|
7044
|
+
process took the bot (409), or `bot.catch` rethrew — grammY's poller does not
|
|
7045
|
+
recover in-process. The resource becomes `failed`, the application stops being
|
|
7046
|
+
ready, and `onEnded` is called once; it is never called for a stop the
|
|
7047
|
+
application asked for. The framework does not exit processes (ADR 0074): the
|
|
7048
|
+
entry point turns `onEnded` into its own shutdown and a non-zero exit so the
|
|
7049
|
+
supervisor starts a fresh process. The Telegram bot template shows the one way.
|
|
7050
|
+
|
|
7051
|
+
**429s** are the application's for its own replies: install
|
|
7052
|
+
`@grammyjs/auto-retry` on `bot.api.config` with a bounded delay. Polling itself
|
|
7053
|
+
already waits Telegram's `retry_after`, and the broadcast and operator channel
|
|
7054
|
+
in `stitchkit/telegram` handle theirs.
|
|
7055
|
+
|
|
7056
|
+
`grammyPollingResource` is the polling half on its own, for a bot that needs no
|
|
7057
|
+
configuration step; it admits updates the same way.
|
|
7029
7058
|
|
|
7030
7059
|
For webhook ingress, `createGrammyWebhookResource` wraps provider-owned update
|
|
7031
7060
|
handling with admission and drain. The application still mounts the HTTP route,
|
|
7032
7061
|
verifies/configures its webhook and chooses the grammY framework adapter. An
|
|
7033
7062
|
update accepted before shutdown may finish; a later update is rejected.
|
|
7034
7063
|
|
|
7035
|
-
The adapter never reads the token
|
|
7064
|
+
The adapter never reads the token or the environment, changes `bot.catch`,
|
|
7036
7065
|
chooses retry plugins, drops pending updates, sends outbound messages or stores
|
|
7037
7066
|
updates. A durable inbox/retry worker remains an application component, not a
|
|
7038
7067
|
mode of this adapter.
|
|
7039
7068
|
|
|
7069
|
+
### A resource that fetches work
|
|
7070
|
+
|
|
7071
|
+
`context.admission` is the application's admission as a resource sees it.
|
|
7072
|
+
`acquire()` answers now; `acquireWhenAccepting(signal)` waits until the
|
|
7073
|
+
application admits and then holds one operation — the right move for a poller or
|
|
7074
|
+
a queue consumer, which should not fetch at all while the application is not
|
|
7075
|
+
ready. It rejects with the signal's reason on abort, and with
|
|
7076
|
+
`ApplicationAdmissionError` once the application is shutting down or failed.
|
|
7077
|
+
|
|
7040
7078
|
## What disappears from an application
|
|
7041
7079
|
|
|
7042
7080
|
A typical small service can replace roughly 120–220 lines of generic signal,
|
|
@@ -10463,6 +10501,9 @@ and our payload is wrong, which is the case a list of substrings quietly counts
|
|
|
10463
10501
|
against the user. An unrecognised refusal leaves the recipient reachable: losing
|
|
10464
10502
|
a working subscriber forever costs more than one wasted send.
|
|
10465
10503
|
|
|
10504
|
+
The rest of what a bot is assembled from — its resources, journal, operator
|
|
10505
|
+
channel, broadcasts and local Bot API files — is in [Telegram bots](telegram.md).
|
|
10506
|
+
|
|
10466
10507
|
## Cookies
|
|
10467
10508
|
|
|
10468
10509
|
```ts
|
|
@@ -12716,6 +12757,152 @@ One vocabulary for every side of the conversation — the server that runs the a
|
|
|
12716
12757
|
page that shows the microphone — so a phase sent over the wire means the same on both ends.
|
|
12717
12758
|
|
|
12718
12759
|
|
|
12760
|
+
==============================================================================
|
|
12761
|
+
# Guide: Telegram bots (docs/guide/telegram.md)
|
|
12762
|
+
==============================================================================
|
|
12763
|
+
|
|
12764
|
+
# Telegram bots
|
|
12765
|
+
|
|
12766
|
+
`stitchkit/telegram` holds what the Bot API itself asks of every bot, with no
|
|
12767
|
+
bot library; `stitchkit/application/grammy` makes a grammY bot a set of
|
|
12768
|
+
application resources. Together they are what a bot is assembled from, so that
|
|
12769
|
+
a bot's own code is its product and nothing else. A generated bot to start from:
|
|
12770
|
+
`bun create stitchkit my-bot --template telegram-bot`. Why it is shaped this
|
|
12771
|
+
way: [ADR 0201](../decisions/0201-a-telegram-bot-is-assembled-from-primitives.md).
|
|
12772
|
+
|
|
12773
|
+
| Job | Where |
|
|
12774
|
+
|---|---|
|
|
12775
|
+
| the bot as resources: command menu, then long polling with updates admitted by batch | `grammyBotResources` — [application kernel](application-kernel.md#optional-grammy-adapter) |
|
|
12776
|
+
| the process journal | `createJsonLogger` from `stitchkit/observability` |
|
|
12777
|
+
| the operator's chat: product events, apart from the journal | `createTelegramOperatorChannel` |
|
|
12778
|
+
| a broadcast that survives crashes, deploys and Ctrl-C | `runTelegramBroadcast` |
|
|
12779
|
+
| a file the local Bot API server wrote to disk | `createTelegramLocalFiles` |
|
|
12780
|
+
| why a send was refused | `classifyTelegramSendFailure` — [auth and errors](auth-and-errors.md#telegram-mini-apps) |
|
|
12781
|
+
| Mini App `initData` | `verifyTelegramInitData` — [auth and errors](auth-and-errors.md#telegram-mini-apps) |
|
|
12782
|
+
| one Bot API call without a bot library | `callTelegramBotApi` |
|
|
12783
|
+
|
|
12784
|
+
## The journal
|
|
12785
|
+
|
|
12786
|
+
A bot's journal is `createJsonLogger`: one JSON object per line on standard
|
|
12787
|
+
output, in pino's shape — numeric `level` (`debug` 20, `info` 30, `warn` 40,
|
|
12788
|
+
`error` 50), `time` in epoch milliseconds, `msg` — so `pino-pretty` reads it on
|
|
12789
|
+
a terminal and a supervisor matching `"level":50` sees errors. Unlike pino, an
|
|
12790
|
+
`Error` under *any* key is written with its name, message, stack and cause; an
|
|
12791
|
+
error logged as `{ error }` does not become `"error":{}`.
|
|
12792
|
+
|
|
12793
|
+
```ts
|
|
12794
|
+
import { createJsonLogger } from 'stitchkit/observability'
|
|
12795
|
+
import { TELEGRAM_BOT_TOKEN_PATTERN } from 'stitchkit/telegram'
|
|
12796
|
+
|
|
12797
|
+
export const logger = createJsonLogger({
|
|
12798
|
+
level: env.LOG_LEVEL,
|
|
12799
|
+
fields: { service: 'my-bot' },
|
|
12800
|
+
sensitiveUrlPatterns: [TELEGRAM_BOT_TOKEN_PATTERN],
|
|
12801
|
+
})
|
|
12802
|
+
|
|
12803
|
+
logger.error('Application startup failed', { error })
|
|
12804
|
+
```
|
|
12805
|
+
|
|
12806
|
+
The error line contract, for whatever recognises a failure from the journal: a
|
|
12807
|
+
line whose `level` is `50` or more is an error. There is no separate `fatal`;
|
|
12808
|
+
an unrecoverable end is an `error` line followed by a non-zero exit.
|
|
12809
|
+
|
|
12810
|
+
## The operator channel
|
|
12811
|
+
|
|
12812
|
+
The chat where people read about new users, payments and failures is not the
|
|
12813
|
+
journal. The journal must not lose a line to a slow network; the chat must not
|
|
12814
|
+
slow the bot down. `post` returns at once and never throws; sends are paced
|
|
12815
|
+
below Telegram's per-chat limit (one every 3 s by default), 429s wait the time
|
|
12816
|
+
Telegram named, and when the chat cannot keep up the oldest message is dropped
|
|
12817
|
+
and reported.
|
|
12818
|
+
|
|
12819
|
+
```ts
|
|
12820
|
+
import { createTelegramOperatorChannel, telegramOperatorSender } from 'stitchkit/telegram'
|
|
12821
|
+
|
|
12822
|
+
const operators = createTelegramOperatorChannel<'users' | 'payments' | 'errors'>({
|
|
12823
|
+
chatId: env.OPERATOR_CHAT_ID,
|
|
12824
|
+
topics: { users: 2, payments: 3, errors: 4 }, // forum message_thread_id
|
|
12825
|
+
send: telegramOperatorSender({ token: env.BOT_TOKEN }),
|
|
12826
|
+
onDropped: (drop) => logger.warn('Operator message dropped', { ...drop }),
|
|
12827
|
+
})
|
|
12828
|
+
|
|
12829
|
+
operators.post(`New user ${user.id}`, 'users')
|
|
12830
|
+
```
|
|
12831
|
+
|
|
12832
|
+
What to post stays the bot's. On the way down, drain it within the grace period
|
|
12833
|
+
and close it: `drain: (context) => operators.drain(context.signal)`,
|
|
12834
|
+
`close: () => operators.close()` in a resource.
|
|
12835
|
+
|
|
12836
|
+
## Broadcasts
|
|
12837
|
+
|
|
12838
|
+
A broadcast is a job with an end, so it is a function, not a resource:
|
|
12839
|
+
|
|
12840
|
+
```ts
|
|
12841
|
+
import { runTelegramBroadcast, telegramBroadcastSender } from 'stitchkit/telegram'
|
|
12842
|
+
|
|
12843
|
+
const report = await runTelegramBroadcast({
|
|
12844
|
+
name: '2026-04-28-price-lock',
|
|
12845
|
+
directory: env.STATE_DIRECTORY, // a state root, never the release tree
|
|
12846
|
+
recipients: () => audience.activeSubscribers(),
|
|
12847
|
+
send: telegramBroadcastSender({
|
|
12848
|
+
token: env.BOT_TOKEN,
|
|
12849
|
+
message: { copyFrom: { chatId: env.DRAFTS_CHAT_ID, messageId: 42 } },
|
|
12850
|
+
}),
|
|
12851
|
+
signal: stop.signal,
|
|
12852
|
+
onProgress: (progress) => logger.info('Broadcast', { ...progress }),
|
|
12853
|
+
})
|
|
12854
|
+
```
|
|
12855
|
+
|
|
12856
|
+
- **Resumable by name.** The audience is written once on the first run; running
|
|
12857
|
+
the same name again continues with that list. Progress is an append-only
|
|
12858
|
+
journal beside it, one line before and one after each send.
|
|
12859
|
+
- **Nobody twice.** A send that was in flight when the process died is recorded
|
|
12860
|
+
`uncertain` and not repeated. A signal stops between sends, after the one in
|
|
12861
|
+
flight is recorded.
|
|
12862
|
+
- **Refusals by meaning.** A blocked, deactivated or never-started recipient is
|
|
12863
|
+
`unreachable` and never addressed again. A 429 waits Telegram's
|
|
12864
|
+
`retry_after` and sends the same recipient again; a server error is retried
|
|
12865
|
+
with backoff up to `maxAttempts`, then `failed`. A message Telegram cannot
|
|
12866
|
+
parse is not the recipient's fault: the run **halts** with them still pending,
|
|
12867
|
+
so the run after the fix reaches them. So does Telegram being unreachable.
|
|
12868
|
+
- **Pacing** is `ratePerSecond`, 25 by default. `dryRun` counts and writes
|
|
12869
|
+
nothing. One runner per name: a second concurrent run is refused.
|
|
12870
|
+
|
|
12871
|
+
The same call works from a script (the process's signal) and inside a live
|
|
12872
|
+
application (the application's signal, with admission held by the caller).
|
|
12873
|
+
The recipient state contains Telegram ids: keep it in the state directory, out
|
|
12874
|
+
of git.
|
|
12875
|
+
|
|
12876
|
+
## Files from a local Bot API server
|
|
12877
|
+
|
|
12878
|
+
A local `telegram-bot-api` keeps downloaded files under `<dir>/<bot token>/`,
|
|
12879
|
+
and a bot sharing that directory reads them from disk and deletes them when
|
|
12880
|
+
done. `file_path` comes from a server over the network, so it is resolved only
|
|
12881
|
+
inside the bot's directory — `../`, an absolute path elsewhere and a link
|
|
12882
|
+
pointing out are refused, judged on real paths.
|
|
12883
|
+
|
|
12884
|
+
```ts
|
|
12885
|
+
import { createTelegramLocalFiles } from 'stitchkit/telegram'
|
|
12886
|
+
|
|
12887
|
+
const files = createTelegramLocalFiles({ root: env.BOT_API_FILES_ROOT, token: env.BOT_TOKEN })
|
|
12888
|
+
|
|
12889
|
+
const { file_path } = await ctx.getFile()
|
|
12890
|
+
const path = await files.resolve(file_path) // throws TelegramLocalFileError
|
|
12891
|
+
// … process it …
|
|
12892
|
+
await files.remove(file_path)
|
|
12893
|
+
```
|
|
12894
|
+
|
|
12895
|
+
The recommended variable for the root is **`BOT_API_FILES_ROOT`** — the
|
|
12896
|
+
server's `--dir`, absolute, as the bot's process sees it. A relative `file_path`
|
|
12897
|
+
(a server without `--local`) and an absolute one inside the bot's directory (a
|
|
12898
|
+
server with `--local`) both resolve. A refusal carries a reason
|
|
12899
|
+
(`root-not-absolute`, `bot-directory-unavailable`, `outside-bot-directory`,
|
|
12900
|
+
`missing`, `not-a-file`) and never the path or the token. `files.check()`
|
|
12901
|
+
answers whether the directory is there to read and delete from; a resource that
|
|
12902
|
+
throws on `{ ready: false }` in `start` keeps the bot from answering before it
|
|
12903
|
+
can read its media.
|
|
12904
|
+
|
|
12905
|
+
|
|
12719
12906
|
==============================================================================
|
|
12720
12907
|
# Guide: GeoIP (docs/guide/geo.md)
|
|
12721
12908
|
==============================================================================
|
|
@@ -12812,6 +12999,43 @@ analytics policy belong to the application.
|
|
|
12812
12999
|
|
|
12813
13000
|
# Upgrading stitchkit
|
|
12814
13001
|
|
|
13002
|
+
## Released migration: 0.96.0
|
|
13003
|
+
|
|
13004
|
+
### resource-context-admission
|
|
13005
|
+
|
|
13006
|
+
One breaking change in `stitchkit/application`: `ManagedResourceContext` gains a
|
|
13007
|
+
required `admission`. Every context the kernel hands a resource already carries
|
|
13008
|
+
it; only a context built by hand needs it.
|
|
13009
|
+
|
|
13010
|
+
1. **Give a hand-built resource context an `admission`.** **Who must act:** a
|
|
13011
|
+
project whose tests call a resource's phase directly with a context object
|
|
13012
|
+
they wrote themselves (`resource.close?.({ applicationId, signal, … })`).
|
|
13013
|
+
The type checker names each one:
|
|
13014
|
+
|
|
13015
|
+
```ts
|
|
13016
|
+
// before
|
|
13017
|
+
await resource.close?.({ applicationId: 'test', signal, now, reportHealth, use })
|
|
13018
|
+
// after
|
|
13019
|
+
await resource.close?.({
|
|
13020
|
+
applicationId: 'test',
|
|
13021
|
+
signal,
|
|
13022
|
+
now,
|
|
13023
|
+
reportHealth,
|
|
13024
|
+
use,
|
|
13025
|
+
admission: { acquire: () => null, acquireWhenAccepting: () => new Promise(() => {}) },
|
|
13026
|
+
})
|
|
13027
|
+
```
|
|
13028
|
+
|
|
13029
|
+
A test that needs real admission starts the resource inside
|
|
13030
|
+
`createApplication` instead.
|
|
13031
|
+
|
|
13032
|
+
2. **Nothing to do for grammY polling, but know what changed.** **Who must
|
|
13033
|
+
act:** nobody. `grammyPollingResource` now admits updates by batch: it does
|
|
13034
|
+
not fetch while the application is not accepting, and finishes the batch in
|
|
13035
|
+
hand before `bot.stop()`. A project that wrote its own `getUpdates`
|
|
13036
|
+
transformer for this (waiting on `app.admission` before returning a batch)
|
|
13037
|
+
deletes it; keeping both waits twice and is harmless.
|
|
13038
|
+
|
|
12815
13039
|
## Released migration: 0.95.0
|
|
12816
13040
|
|
|
12817
13041
|
One breaking change in `stitchkit/application`: the process lifecycle ledger
|
|
@@ -17111,6 +17335,7 @@ cutovers are covered by the executable
|
|
|
17111
17335
|
| `ApplicationConfig` / `ApplicationHandle` | _type_ | application declaration and its start/snapshot/subscription/admission/shutdown handle |
|
|
17112
17336
|
| `ApplicationAdmission` / `ApplicationOperationLease` | _type_ | atomic process-local admission and idempotent release primitive |
|
|
17113
17337
|
| `ManagedResource` / `ManagedResourceContext` / `ManagedResourceStartResult` | _type_ | resource lifecycle callbacks, shared deadlines, health reporting, separate readiness/completion promises and the value a resource publishes to its dependants |
|
|
17338
|
+
| `ManagedResourceAdmission` | _type_ | `context.admission`: `acquire()` now or `null`, and `acquireWhenAccepting(signal)` for a resource that fetches work — a poller waits instead of fetching while the application is starting or degraded; rejects on abort and once the application can no longer admit |
|
|
17114
17339
|
| `ManagedResourceDependency` | _type_ | a dependency named by id or given as the resource itself — the second form is what `context.use(...)` can type |
|
|
17115
17340
|
| `ManagedResourcePublished` | _type_ | the value type `context.use(resource)` returns, recovered from that resource's own `start` |
|
|
17116
17341
|
| `ManagedResourcePublishesNoValue` | _type_ | what `context.use(...)` returns for a resource that publishes nothing — a branded refusal rather than `never`, so reading it does not silently compile |
|
|
@@ -17288,6 +17513,10 @@ does not resolve grammY.
|
|
|
17288
17513
|
| Export | Kind | Summary |
|
|
17289
17514
|
|--------|------|---------|
|
|
17290
17515
|
| `grammyPollingResource` | function | adapt an injected bot's long polling with distinct `onStart` readiness, observed completion and one stop chain |
|
|
17516
|
+
| `grammyBotResources` | function | a long-polling bot as `telegram-configuration` (optional `configure`, e.g. `setMyCommands`) then `telegram-polling`, both after the bot's `dependsOn`; updates admitted by batch and the end of polling reported to `onEnded` |
|
|
17517
|
+
| `GrammyBotResourcesConfig` / `GrammyBotResources` | _type_ | injected bot, `dependsOn`, `configure`, polling options, `onStart` / `onError` / `onEnded`; `{ configuration?, polling, resources }` in start order |
|
|
17518
|
+
| `TELEGRAM_CONFIGURATION_RESOURCE_ID` / `TELEGRAM_POLLING_RESOURCE_ID` | constant | the two stable ids a monitor, a test or a restart addresses |
|
|
17519
|
+
| `GrammyPollingEnd` | _type_ | `{ error? }` — how a poller that was ready ended on its own; never reported for a requested stop |
|
|
17291
17520
|
| `createGrammyWebhookResource` | function | return a managed resource plus admission-guarded `handleUpdate`; HTTP hosting and webhook registration stay application-owned |
|
|
17292
17521
|
| `GrammyWebhookUnavailableError` | class | stable `GRAMMY_WEBHOOK_NOT_ACCEPTING` rejection after webhook admission closes |
|
|
17293
17522
|
| `GrammyPollingResourceConfig` | _type_ | injected bot, polling options, resource graph policy and isolated error observer |
|
|
@@ -17817,6 +18046,8 @@ audit event. See the [Observability guide](../guide/observability.md).
|
|
|
17817
18046
|
| `DimensionCollision` / `SetRequestDimensionsOptions` | _type_ | explicit overwrite, preserve or error policy for dimension keys |
|
|
17818
18047
|
| `createBoundedLogger` | function | decorate a `StitchLogger` with request context, shared sanitisation, redaction and total bounds |
|
|
17819
18048
|
| `DEFAULT_REDACT_PATHS` | constant | baseline credential/token paths added to the sanitizer's sensitive-key policy |
|
|
18049
|
+
| `createJsonLogger` | function | the process journal: one JSON line per call in pino's shape (numeric `level`, epoch `time`, `msg`) on standard output, through `createBoundedLogger` — an `Error` in any field keeps name, message, stack and cause; secrets masked; `level` threshold, static `fields`, injectable `write` |
|
|
18050
|
+
| `JsonLoggerOptions` / `JsonLogLevel` / `JSON_LOG_LEVELS` | _type_ / constant | the bounded-logger options plus `level`, `fields`, `write`, `now`; pino's numbers `debug` 20 … `error` 50, which a line reader matches on |
|
|
17820
18051
|
| `BoundedLoggerBounds` / `BoundedLoggerOptions` | _type_ | per-value and total record ceilings (`stringLengthByKey` raises the string bound for named keys such as `stack`) plus sink/redaction configuration |
|
|
17821
18052
|
| `RequestEvent` | _type_ | the normalised audit event handed to the sink; opt-in HTTP cancellation rows carry `outcome: 'cancelled'` |
|
|
17822
18053
|
| `runUnitOfWork` / `RunUnitOfWorkOptions` | function / _type_ | run work that did not arrive over a transport — an agent loop, a scheduled broadcast — inside one context, with the same completion record a request gets |
|
|
@@ -18289,9 +18520,10 @@ available from `stitchkit/contract`.
|
|
|
18289
18520
|
|
|
18290
18521
|
## `stitchkit/telegram`
|
|
18291
18522
|
|
|
18292
|
-
Peer-free server-only Telegram platform primitives
|
|
18523
|
+
Peer-free server-only Telegram platform primitives — the Bot API's own
|
|
18524
|
+
protocols and the jobs every bot rewrote around them. Importing this resolves no
|
|
18293
18525
|
bot library; `stitchkit/application/grammy` remains the lifecycle adapter for an
|
|
18294
|
-
injected grammY bot. → ADR 0143
|
|
18526
|
+
injected grammY bot. → ADR 0143, ADR 0201 — [guide](../guide/telegram.md)
|
|
18295
18527
|
|
|
18296
18528
|
| Export | Kind | Summary |
|
|
18297
18529
|
|--------|------|---------|
|
|
@@ -18304,6 +18536,16 @@ injected grammY bot. → ADR 0143
|
|
|
18304
18536
|
| `classifyTelegramSendFailure` | function | name a refused Bot API send and separate "retry this send" from "stop addressing this recipient" |
|
|
18305
18537
|
| `TelegramSendFailure` | _type_ | reason, `status`, Telegram-stated `retryAfterSeconds`, `retryable`, `recipientUnreachable` and which evidence produced the answer |
|
|
18306
18538
|
| `TelegramSendFailureReason` | _type_ | `blocked-by-user` / `user-deactivated` / `chat-not-found` / `not-started` / `rate-limited` / `message-invalid` / `server-error` / `unknown` |
|
|
18539
|
+
| `runTelegramBroadcast` | function | a resumable broadcast by `name` under a state `directory`: audience written once, an append-only journal, pacing at `ratePerSecond` (25), 429 waits `retry_after`, unreachable recipients never addressed again, a `message-invalid` or unreachable Telegram halts without charging the recipient, a send in flight at a crash is `uncertain` and not repeated, `dryRun`, one runner per name |
|
|
18540
|
+
| `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` |
|
|
18541
|
+
| `telegramBroadcastSender` / `TelegramBroadcastSenderConfig` / `TelegramBroadcastMessage` | function / _type_ | the standard `send`: `sendMessage` with a text or `copyMessage` of a prepared message, through the Bot API |
|
|
18542
|
+
| `createTelegramOperatorChannel` | function | the operator chat, apart from the journal: `post(text, topic?)` returns at once and never throws; paced sends into forum topics, 429 honoured, oldest dropped on overflow, `drain(signal)` and `close()` for the way down |
|
|
18543
|
+
| `TelegramOperatorChannelConfig` / `TelegramOperatorChannel` / `TelegramOperatorMessage` / `TelegramOperatorDrop` / `TelegramChatId` | _type_ | `chatId`, `topics`, `send`, `maxQueued`, `minIntervalMs`, `maxAttempts`, `onDropped`; a drop is `overflow` / `refused` / `attempts` / `closed` with its classification |
|
|
18544
|
+
| `telegramOperatorSender` / `TelegramOperatorSenderConfig` | function / _type_ | the standard `send`: `sendMessage` into the chat and topic, plain text unless `parseMode` |
|
|
18545
|
+
| `createTelegramLocalFiles` | function | files the local Bot API server wrote under `<root>/<token>/`: `resolve(file_path)` and `remove(file_path)` only inside the bot's directory, judged on real paths so a link is judged by where it leads; `check()` for readiness. Recommended root variable: `BOT_API_FILES_ROOT` |
|
|
18546
|
+
| `TelegramLocalFilesConfig` / `TelegramLocalFiles` / `TelegramLocalFilesCheck` / `TelegramLocalFileError` / `TelegramLocalFileRefusal` | _type_ / class | `root`, `token`; refusals `root-not-absolute` / `bot-directory-unavailable` / `outside-bot-directory` / `missing` / `not-a-file`, never carrying the path or the token |
|
|
18547
|
+
| `callTelegramBotApi` / `TelegramBotApiCall` / `TelegramBotApiError` | function / _type_ / class | one Bot API method over `fetch`; a refusal keeps `error_code`, `description` and `parameters` for the classifier, and no error names the URL |
|
|
18548
|
+
| `TELEGRAM_BOT_TOKEN_PATTERN` | constant | the shape of a bot token, for a logger's `sensitiveUrlPatterns` |
|
|
18307
18549
|
|
|
18308
18550
|
---
|
|
18309
18551
|
|
package/llms.txt
CHANGED
|
@@ -9,7 +9,7 @@ Build with stitchkit: define a contract once, then `implement` it and serve it (
|
|
|
9
9
|
Load the slice for each entrypoint your code imports — `import … from 'stitchkit/server'` → `llms/server.txt`; the root `stitchkit` import → `llms/stitchkit.txt`. A slice holds that entrypoint's API reference section and the guide pages whose home it is; every guide lives in exactly one slice, and a slice that needs another's guide names it under "Also read". Each file is at most 50.0 KB; a longer one continues in numbered parts, each naming the next at its top. Moving across versions: load `llms/upgrading.txt` and the range slices it lists between your version and the target. `llms-full.txt` inlines everything; it is far larger than a context window, so do not read it whole.
|
|
10
10
|
|
|
11
11
|
## Entrypoint slices
|
|
12
|
-
- **stitchkit**: [llms/stitchkit.txt](llms/stitchkit.txt) 15.
|
|
12
|
+
- **stitchkit**: [llms/stitchkit.txt](llms/stitchkit.txt) 15.6 KB → [llms/stitchkit.2.txt](llms/stitchkit.2.txt) 35.0 KB → [llms/stitchkit.3.txt](llms/stitchkit.3.txt) 14.2 KB
|
|
13
13
|
- **stitchkit/live**: [llms/live.txt](llms/live.txt) 21.2 KB
|
|
14
14
|
- **stitchkit/react**: [llms/react.txt](llms/react.txt) 8.6 KB
|
|
15
15
|
- **stitchkit/tools**: [llms/tools.txt](llms/tools.txt) 44.9 KB → [llms/tools.2.txt](llms/tools.2.txt) 26.3 KB → [llms/tools.3.txt](llms/tools.3.txt) 25.4 KB
|
|
@@ -20,8 +20,8 @@ Load the slice for each entrypoint your code imports — `import … from 'stitc
|
|
|
20
20
|
- **stitchkit/remote**: [llms/remote.txt](llms/remote.txt) 1.0 KB
|
|
21
21
|
- **stitchkit/contract**: [llms/contract.txt](llms/contract.txt) 31.9 KB
|
|
22
22
|
- **stitchkit/primitives**: [llms/primitives.txt](llms/primitives.txt) 9.3 KB
|
|
23
|
-
- **stitchkit/server**: [llms/server.txt](llms/server.txt) 45.3 KB → [llms/server.2.txt](llms/server.2.txt) 14.6 KB → [llms/server.3.txt](llms/server.3.txt) 42.7 KB → [llms/server.4.txt](llms/server.4.txt) 37.
|
|
24
|
-
- **stitchkit/observability**: [llms/observability.txt](llms/observability.txt)
|
|
23
|
+
- **stitchkit/server**: [llms/server.txt](llms/server.txt) 45.3 KB → [llms/server.2.txt](llms/server.2.txt) 14.6 KB → [llms/server.3.txt](llms/server.3.txt) 42.7 KB → [llms/server.4.txt](llms/server.4.txt) 37.5 KB → [llms/server.5.txt](llms/server.5.txt) 21.8 KB
|
|
24
|
+
- **stitchkit/observability**: [llms/observability.txt](llms/observability.txt) 43.4 KB
|
|
25
25
|
- **stitchkit/agent-runtime**: [llms/agent-runtime.txt](llms/agent-runtime.txt) 46.6 KB → [llms/agent-runtime.2.txt](llms/agent-runtime.2.txt) 29.8 KB → [llms/agent-runtime.3.txt](llms/agent-runtime.3.txt) 31.8 KB
|
|
26
26
|
- **stitchkit/agent-runtime/testing**: [llms/agent-runtime-testing.txt](llms/agent-runtime-testing.txt) 2.2 KB
|
|
27
27
|
- **stitchkit/agent-runtime/harness**: [llms/agent-runtime-harness.txt](llms/agent-runtime-harness.txt) 4.1 KB
|
|
@@ -30,8 +30,8 @@ Load the slice for each entrypoint your code imports — `import … from 'stitc
|
|
|
30
30
|
- **stitchkit/agent-runtime/openrouter**: [llms/agent-runtime-openrouter.txt](llms/agent-runtime-openrouter.txt) 1.1 KB
|
|
31
31
|
- **stitchkit/agent-runtime/sqlite/bun**: [llms/agent-runtime-sqlite-bun.txt](llms/agent-runtime-sqlite-bun.txt) 1.8 KB
|
|
32
32
|
- **stitchkit/agent-runtime/sqlite/node**: [llms/agent-runtime-sqlite-node.txt](llms/agent-runtime-sqlite-node.txt) 1.7 KB
|
|
33
|
-
- **stitchkit/application**: [llms/application.txt](llms/application.txt)
|
|
34
|
-
- **stitchkit/application/grammy**: [llms/application-grammy.txt](llms/application-grammy.txt)
|
|
33
|
+
- **stitchkit/application**: [llms/application.txt](llms/application.txt) 44.6 KB → [llms/application.2.txt](llms/application.2.txt) 32.9 KB
|
|
34
|
+
- **stitchkit/application/grammy**: [llms/application-grammy.txt](llms/application-grammy.txt) 2.3 KB
|
|
35
35
|
- **stitchkit/application/opentelemetry**: [llms/application-opentelemetry.txt](llms/application-opentelemetry.txt) 1.6 KB
|
|
36
36
|
- **stitchkit/application/directory-inbox**: [llms/application-directory-inbox.txt](llms/application-directory-inbox.txt) 0.3 KB
|
|
37
37
|
- **stitchkit/application/diagnostic-journal**: [llms/application-diagnostic-journal.txt](llms/application-diagnostic-journal.txt) 1.4 KB
|
|
@@ -39,7 +39,7 @@ Load the slice for each entrypoint your code imports — `import … from 'stitc
|
|
|
39
39
|
- **stitchkit/testing**: [llms/testing.txt](llms/testing.txt) 30.8 KB
|
|
40
40
|
- **stitchkit/agent-runtime/sandbox**: [llms/agent-runtime-sandbox.txt](llms/agent-runtime-sandbox.txt) 8.1 KB
|
|
41
41
|
- **stitchkit/files**: [llms/files.txt](llms/files.txt) 2.9 KB
|
|
42
|
-
- **stitchkit/telegram**: [llms/telegram.txt](llms/telegram.txt)
|
|
42
|
+
- **stitchkit/telegram**: [llms/telegram.txt](llms/telegram.txt) 11.7 KB
|
|
43
43
|
- **stitchkit/tracking**: [llms/tracking.txt](llms/tracking.txt) 16.8 KB
|
|
44
44
|
- **stitchkit/release**: [llms/release.txt](llms/release.txt) 8.9 KB
|
|
45
45
|
- **stitchkit/tracking/server**: [llms/tracking-server.txt](llms/tracking-server.txt) 2.5 KB
|
|
@@ -52,9 +52,9 @@ Load the slice for each entrypoint your code imports — `import … from 'stitc
|
|
|
52
52
|
|
|
53
53
|
## Upgrading slices
|
|
54
54
|
- **Upgrading — how to move across versions**: [llms/upgrading.txt](llms/upgrading.txt) 10.3 KB
|
|
55
|
-
- **Upgrading 0.
|
|
56
|
-
- **Upgrading 0.
|
|
57
|
-
- **Upgrading 0.37.0 → 0.
|
|
55
|
+
- **Upgrading 0.70.0 → 0.96.0**: [llms/upgrading-0.70.0-0.96.0.txt](llms/upgrading-0.70.0-0.96.0.txt) 46.3 KB
|
|
56
|
+
- **Upgrading 0.57.0 → 0.69.0**: [llms/upgrading-0.57.0-0.69.0.txt](llms/upgrading-0.57.0-0.69.0.txt) 44.4 KB
|
|
57
|
+
- **Upgrading 0.37.0 → 0.56.0**: [llms/upgrading-0.37.0-0.56.0.txt](llms/upgrading-0.37.0-0.56.0.txt) 36.9 KB
|
|
58
58
|
|
|
59
59
|
## Guide (web)
|
|
60
60
|
- [Getting started](https://github.com/max-listov/stitchkit/blob/master/docs/guide/getting-started.md): install, entrypoints, and a first contract → server → client app
|
|
@@ -80,6 +80,7 @@ Load the slice for each entrypoint your code imports — `import … from 'stitc
|
|
|
80
80
|
- [Frontend integrations](https://github.com/max-listov/stitchkit/blob/master/docs/guide/frontend-integrations.md): React Router resource routes and a separate Vite development proxy
|
|
81
81
|
- [React Query policy](https://github.com/max-listov/stitchkit/blob/master/docs/guide/react.md): request-local SSR clients, a browser singleton and ApiError retry rules
|
|
82
82
|
- [Voice](https://github.com/max-listov/stitchkit/blob/master/docs/guide/voice.md): stitchkit/voice — sentences cut while a reply streams, the text a voice reads, a speech queue that synthesises ahead and stops on interruption
|
|
83
|
+
- [Telegram bots](https://github.com/max-listov/stitchkit/blob/master/docs/guide/telegram.md): stitchkit/telegram — the journal, the operator channel, resumable broadcasts, local Bot API files; the bot as application resources
|
|
83
84
|
- [GeoIP](https://github.com/max-listov/stitchkit/blob/master/docs/guide/geo.md): stitchkit/geo — a server-only managed reader, three observable states and last-known-good generation reloads
|
|
84
85
|
- [Upgrading](https://github.com/max-listov/stitchkit/blob/master/docs/guide/upgrading.md): moving a project across stitchkit versions; how breaking changes are marked
|
|
85
86
|
- [Project declaration](https://github.com/max-listov/stitchkit/blob/master/docs/guide/declaration.md): the optional machine-readable statement a repository makes about itself — identity, roles, build, requirements, release steps and the names of the values a deployment supplies
|
package/package.json
CHANGED