stitchkit 0.90.4 → 0.90.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/CHANGELOG.md +102 -0
  2. package/README.md +2 -0
  3. package/dist/agent-runtime-coding-tools.js +5 -4
  4. package/dist/agent-runtime-harness.js +6 -5
  5. package/dist/agent-runtime-sandbox.js +5 -4
  6. package/dist/agent-runtime.js +9 -8
  7. package/dist/application/grammy.d.ts.map +1 -1
  8. package/dist/application-grammy.js +4 -1
  9. package/dist/cli.js +62 -18
  10. package/dist/contract/index.js +2 -1
  11. package/dist/google.d.ts +56 -0
  12. package/dist/google.d.ts.map +1 -0
  13. package/dist/google.js +175 -0
  14. package/dist/{index-32vjke6q.js → index-1bnkzq95.js} +1 -1
  15. package/dist/{index-db51n3xr.js → index-640tz39q.js} +5 -5
  16. package/dist/{index-19ryv24q.js → index-83fafqw8.js} +3 -3
  17. package/dist/{index-z78bjcxj.js → index-aj9geeez.js} +1 -1
  18. package/dist/{index-bg8ypxzn.js → index-bva395we.js} +6 -7
  19. package/dist/{index-y47z614h.js → index-fqg5mfk7.js} +145 -32
  20. package/dist/index-hr4tzbwq.js +11 -0
  21. package/dist/{index-kazec06k.js → index-nrqg8tks.js} +5 -19
  22. package/dist/index-rxfy4cq7.js +17 -0
  23. package/dist/{index-6atvfjc1.js → index-s208wab5.js} +1 -1
  24. package/dist/{index-7t0wq6j5.js → index-tgh3ksfh.js} +5 -5
  25. package/dist/{index-6q3zjwaz.js → index-vy2nxwg8.js} +1 -1
  26. package/dist/{index-kzxpsf8y.js → index-zsdgd1tz.js} +1 -1
  27. package/dist/index.js +10 -9
  28. package/dist/internal/pkce.d.ts +3 -0
  29. package/dist/internal/pkce.d.ts.map +1 -0
  30. package/dist/node.js +5 -4
  31. package/dist/oauth.d.ts +57 -0
  32. package/dist/oauth.d.ts.map +1 -0
  33. package/dist/oauth.js +170 -0
  34. package/dist/observability/index.js +3 -2
  35. package/dist/primitives.js +2 -1
  36. package/dist/remote.js +4 -3
  37. package/dist/server/bun.d.ts.map +1 -1
  38. package/dist/server/index.js +14 -11
  39. package/dist/server/middleware/pkce.d.ts +1 -2
  40. package/dist/server/middleware/pkce.d.ts.map +1 -1
  41. package/dist/testing.js +5 -4
  42. package/dist/tool-invoker.js +7 -6
  43. package/dist/tools/cli-args.d.ts +10 -8
  44. package/dist/tools/cli-args.d.ts.map +1 -1
  45. package/dist/tools/cli-installer.d.ts +7 -2
  46. package/dist/tools/cli-installer.d.ts.map +1 -1
  47. package/dist/tools/cli-view.d.ts.map +1 -1
  48. package/dist/tools/cli.d.ts.map +1 -1
  49. package/dist/tools/connections/index.js +37 -4
  50. package/dist/tools/connections/mcp-envelope.d.ts +44 -0
  51. package/dist/tools/connections/mcp-envelope.d.ts.map +1 -0
  52. package/dist/tools/connections/mcp.d.ts.map +1 -1
  53. package/dist/tools.js +16 -14
  54. package/dist/tracking.js +3 -2
  55. package/llms-full.txt +224 -8
  56. package/llms.txt +1 -0
  57. package/package.json +36 -22
  58. package/dist/{index-8crm1srv.js → index-3yqgj323.js} +3 -3
  59. package/dist/{index-vcnfwrtr.js → index-8pjqv3zh.js} +6 -6
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-envelope.d.ts","sourceRoot":"","sources":["../../../src/tools/connections/mcp-envelope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAIjD,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAaxD;AAED;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,wBAAwB,CAAC;AAEzD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,QAAQ,CAa1E"}
@@ -1 +1 @@
1
- {"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../../../src/tools/connections/mcp.ts"],"names":[],"mappings":"AAEA,OAAO,EAAqB,KAAK,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAGhF,OAAO,EACL,KAAK,0BAA0B,EAIhC,MAAM,WAAW,CAAC;AAGnB,OAAO,KAAK,EAAE,mBAAmB,EAAiB,MAAM,SAAS,CAAC;AAQlE,gEAAgE;AAChE,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,mBAAmB,EAC/B,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,0BAA0B,GACpC,OAAO,CAAC,qBAAqB,EAAE,CAAC,CAgElC"}
1
+ {"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../../../src/tools/connections/mcp.ts"],"names":[],"mappings":"AAEA,OAAO,EAAqB,KAAK,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAIhF,OAAO,EACL,KAAK,0BAA0B,EAIhC,MAAM,WAAW,CAAC;AAGnB,OAAO,KAAK,EAAE,mBAAmB,EAAiB,MAAM,SAAS,CAAC;AAQlE,gEAAgE;AAChE,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,mBAAmB,EAC/B,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,0BAA0B,GACpC,OAAO,CAAC,qBAAqB,EAAE,CAAC,CAqElC"}
package/dist/tools.js CHANGED
@@ -4,7 +4,8 @@ import {
4
4
  import {
5
5
  signJwt,
6
6
  verifyPkce
7
- } from "./index-bg8ypxzn.js";
7
+ } from "./index-bva395we.js";
8
+ import"./index-hr4tzbwq.js";
8
9
  import {
9
10
  DEFAULT_CORS_ALLOW_HEADERS,
10
11
  DEFAULT_PROCESS_SIGNALS,
@@ -13,7 +14,7 @@ import {
13
14
  guardSignalCallback,
14
15
  mediaTypeEssence,
15
16
  reportSignalError
16
- } from "./index-z78bjcxj.js";
17
+ } from "./index-aj9geeez.js";
17
18
  import {
18
19
  redact
19
20
  } from "./index-xbppf54a.js";
@@ -21,7 +22,7 @@ import {
21
22
  buildToolManifest,
22
23
  describeToolCatalog,
23
24
  mountAgent
24
- } from "./index-19ryv24q.js";
25
+ } from "./index-83fafqw8.js";
25
26
  import"./index-7rkhw9ec.js";
26
27
  import {
27
28
  argumentsDigest
@@ -37,7 +38,7 @@ import {
37
38
  } from "./index-4fpa9dy2.js";
38
39
  import {
39
40
  createToolInvoker
40
- } from "./index-7t0wq6j5.js";
41
+ } from "./index-tgh3ksfh.js";
41
42
  import {
42
43
  WaitTimeoutError,
43
44
  createCli,
@@ -46,22 +47,22 @@ import {
46
47
  fetchPinnedDocument,
47
48
  readCapped,
48
49
  runWaitOperation
49
- } from "./index-y47z614h.js";
50
+ } from "./index-fqg5mfk7.js";
50
51
  import"./index-sbdmyz75.js";
51
52
  import {
52
53
  collectToolSurface
53
- } from "./index-kzxpsf8y.js";
54
+ } from "./index-zsdgd1tz.js";
54
55
  import {
55
56
  collectTools,
56
57
  createToolRunner,
57
58
  formatToolError
58
- } from "./index-32vjke6q.js";
59
+ } from "./index-1bnkzq95.js";
59
60
  import {
60
61
  ToolExecutionControlError,
61
62
  executeToolMethod,
62
63
  isToolExecutionControlError,
63
64
  toolResultFromError
64
- } from "./index-vcnfwrtr.js";
65
+ } from "./index-8pjqv3zh.js";
65
66
  import {
66
67
  getRequestContext,
67
68
  getTraceId,
@@ -73,22 +74,18 @@ import {
73
74
  } from "./index-2hryh65w.js";
74
75
  import {
75
76
  defineContract
76
- } from "./index-kazec06k.js";
77
+ } from "./index-nrqg8tks.js";
78
+ import"./index-rxfy4cq7.js";
77
79
  import {
78
80
  ManagedFilePathSchema,
79
81
  ManagedFileRefSchema
80
82
  } from "./index-6k1937bx.js";
81
- import {
82
- AppError,
83
- STITCH_ERROR_STATUS
84
- } from "./index-scs3f1eg.js";
85
83
  import {
86
84
  resolvePropagationContext
87
85
  } from "./index-zcgf3gqf.js";
88
86
  import {
89
87
  coerceJsonArgs
90
88
  } from "./index-7zbps32p.js";
91
- import"./index-kzfs85xp.js";
92
89
  import {
93
90
  createRuntimeToolFactory,
94
91
  defineRuntimeTool
@@ -105,6 +102,11 @@ import {
105
102
  validateMcpRoundPolicy
106
103
  } from "./index-fenaekmk.js";
107
104
  import"./index-cby4ar3v.js";
105
+ import {
106
+ AppError,
107
+ STITCH_ERROR_STATUS
108
+ } from "./index-scs3f1eg.js";
109
+ import"./index-kzfs85xp.js";
108
110
  import {
109
111
  isRecord
110
112
  } from "./index-77fekveh.js";
package/dist/tracking.js CHANGED
@@ -3,10 +3,11 @@ import {
3
3
  } from "./index-3xwxfj4z.js";
4
4
  import {
5
5
  defineContract
6
- } from "./index-kazec06k.js";
6
+ } from "./index-nrqg8tks.js";
7
+ import"./index-rxfy4cq7.js";
7
8
  import"./index-6k1937bx.js";
8
- import"./index-scs3f1eg.js";
9
9
  import"./index-zcgf3gqf.js";
10
+ import"./index-scs3f1eg.js";
10
11
  import"./index-kzfs85xp.js";
11
12
  import"./index-77fekveh.js";
12
13
 
package/llms-full.txt CHANGED
@@ -64,6 +64,8 @@ own, recorded as an ADR.
64
64
  | `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 |
65
65
  | `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 |
66
66
  | `stitchkit/geo` | server (Bun or Node) | evolving | managed GeoIP reader generations, last-known-good reload and the optional MaxMind adapter |
67
+ | `stitchkit/oauth` | browser **and** server | evolving | provider-neutral Authorization Code + PKCE request and one-shot callback transaction mechanics |
68
+ | `stitchkit/google` | server (Bun or Node) | evolving | optional Google code exchange and verified OIDC identity adapter |
67
69
  | `stitchkit/observability` | server | stable<br>_redefined in 1 of the 35 minors since 0.56.2, most recently 0.83.0_ | request/tool event projections — `createObservability`, trace context, sanitisation |
68
70
  | `stitchkit/testing` | tests on Bun or Node | stable | in-process generated clients over a real Fetch handler, plus the store and managed-resource conformance kits |
69
71
  | `stitchkit/declaration` | browser + build and deployment tooling (Bun or Node) | evolving | `ProjectDeclarationSchema` — the one machine-readable statement a repository makes about itself |
@@ -190,6 +192,8 @@ map — feature → packages:
190
192
  | MCP Apps UI widgets | `@modelcontextprotocol/ext-apps` |
191
193
  | React data layer (`stitchkit/react`) | `@tanstack/react-query` `react-query-kit` |
192
194
  | MaxMind GeoIP (`stitchkit/geo`) | `maxmind` |
195
+ | Browser OAuth client (`stitchkit/oauth`) | — |
196
+ | Google OIDC verifier (`stitchkit/google`) | `google-auth-library` |
193
197
  | **Socket.IO server on Bun** | `socket.io` `@socket.io/bun-engine` |
194
198
  | **Socket.IO server on Node** | `socket.io` |
195
199
  | Socket.IO client | `socket.io-client` (runtime peer; unrelated root declarations remain peer-free) |
@@ -226,6 +230,7 @@ package. MCP hosts and client E2E additionally install
226
230
  - [MCP & agents](./mcp-and-agents.md) — contracts as AI tools.
227
231
  - [Realtime](./realtime.md) — Socket.IO and the cache bridge.
228
232
  - [Auth & errors](./auth-and-errors.md) — scopes, auth hooks, the error model.
233
+ - [Browser OAuth and Google OIDC](./oauth.md) — PKCE transaction and verified provider identity.
229
234
  - [Testing & deployment](./testing-and-deployment.md).
230
235
  - [API reference](../api/reference.md) — every export, by entrypoint.
231
236
 
@@ -7489,9 +7494,12 @@ same Zod schema an HTTP or MCP call does.
7489
7494
  | `--quiet` | Suppress non-essential stderr output |
7490
7495
  | `--dry-run` | Print the resolved call without executing |
7491
7496
  | `--help`, `-h` | Usage — top-level or per-command flag table |
7497
+ | `--help <text>` | List only the commands matching a substring |
7492
7498
  | `--count-by <field>` | Count records per distinct value — see [Aggregate views](#aggregate-views) |
7493
7499
  | `--sum <f> [--by <g>]`| Total a numeric field, optionally grouped |
7494
- | `--top <n> --by <f>` | Keep only the n largest groups |
7500
+ | `--sort <field>` | Order records by a field, largest first |
7501
+ | `--ascending` | Flip `--sort` to smallest first |
7502
+ | `--top <n>` | Keep the n leading entries of the view asked for |
7495
7503
  | `--table <a,b>` | Render named fields as an aligned table |
7496
7504
 
7497
7505
  stdout carries the result; structured errors and progress go to stderr. With
@@ -7502,6 +7510,34 @@ diagnostics remain ordinary stderr text. This keeps stdout pipeable and
7502
7510
  `VALIDATION_ERROR → 1`, `UNAUTHORIZED → 2`, `FORBIDDEN → 3`, `NOT_FOUND → 4`,
7503
7511
  …) — override per app with `exitCodes`.
7504
7512
 
7513
+ ### A narrower question than "all of them"
7514
+
7515
+ On a discovered surface `--help` is the only way to learn what exists, and that
7516
+ can be two hundred commands. At that size the list stops being an answer: it
7517
+ scrolls past a person and costs an agent the same context an unfiltered result
7518
+ would. So there is a question between "one command" and "all of them":
7519
+
7520
+ ```bash
7521
+ myapp --help broadcast # also: -h broadcast · help broadcast · --help=broadcast
7522
+ ```
7523
+
7524
+ ```
7525
+ Commands matching "broadcast" (3 of 205):
7526
+ broadcast_send Send a broadcast to every subscriber
7527
+ broadcast_cancel Stop a running broadcast
7528
+ announce_publish Publish an announcement as a broadcast
7529
+ ```
7530
+
7531
+ The description is searched as well as the name, because the word someone knows
7532
+ is often in the sentence rather than the name — `announce_publish` above is
7533
+ matched that way. The count says what was left out.
7534
+
7535
+ **No match is an exit code**, not an empty success: `0` over an empty list reads
7536
+ as "there are none", which is a different statement from "none of these". It
7537
+ exits with whatever `NOT_FOUND` maps to (`4` by default). Bare `--help` is
7538
+ unchanged, and `--help=false` still means what it always did — the reserved
7539
+ boolean's negation — so one value never carries two meanings.
7540
+
7505
7541
  ## Application global options
7506
7542
 
7507
7543
  `--json` and friends above are the framework's. An application usually has
@@ -7632,9 +7668,20 @@ myapp item_list --top 5 --by status # same view, written the other way ro
7632
7668
  myapp item_list --table id,status # the one human-facing shape
7633
7669
  ```
7634
7670
 
7635
- `--by` always names the **grouping** field, in every form it appears in, so the
7636
- grammar has one meaning rather than two. Groups are ordered largest first, which
7637
- is what makes `--top` a defined slice rather than an arbitrary one.
7671
+ Two words, one each: **`--by` groups, `--sort` orders.** That leaves `--top` a
7672
+ single meaning everywhere *the n leading entries of the view you asked for* —
7673
+ so the question a CLI actually gets asked composes out of the parts:
7674
+
7675
+ ```bash
7676
+ myapp item_list --top 5 --sort messages --table id,messages # the five biggest, as a table
7677
+ myapp item_list --sort messages --top 5 --json # the same five, as records
7678
+ myapp item_list --sort name --ascending # ordered the other way
7679
+ ```
7680
+
7681
+ Groups come back largest first for the same reason. A record that carries no
7682
+ value for the sort field sorts **last in both directions**: it is not the
7683
+ smallest, it is not on the scale at all, and letting it lead an ascending list
7684
+ would answer a question nobody asked.
7638
7685
 
7639
7686
  Three rules worth knowing before you rely on them:
7640
7687
 
@@ -7646,6 +7693,8 @@ Three rules worth knowing before you rely on them:
7646
7693
  over an object with two array fields, is refused rather than guessed.
7647
7694
  - **Without a view flag the output is byte-for-byte what it was.** The flags are
7648
7695
  reserved CLI behaviour like `--json`; they never reach a tool argument.
7696
+ - **Ordering and grouping do not mix.** `--sort` with `--by`, `--count-by` or
7697
+ `--sum` is refused rather than given a second meaning.
7649
7698
 
7650
7699
  A failed call still reports its own error and exit code. An aggregate over an
7651
7700
  error is not an answer to the question that was asked.
@@ -7725,8 +7774,12 @@ import {
7725
7774
  // otherwise everyone who already installed it never receives the fix.
7726
7775
  assertCliPublishable(previous, next)
7727
7776
 
7728
- // Serving: one generated script per target, no JSON on the wire.
7729
- renderCliInstaller({ manifest, asset: selectCliBuildAsset(manifest, target)!, binaryName: 'myapp' })
7777
+ // Serving: omit `asset` and one script covers every published target, selecting
7778
+ // by uname at run time otherwise that dispatch is the last hand-written piece
7779
+ // of the install path, and every publisher writes the same x86_64 → x64 table.
7780
+ renderCliInstaller({ manifest, binaryName: 'myapp' })
7781
+ // Or pin one target explicitly:
7782
+ renderCliInstaller({ manifest, asset: selectCliBuildAsset(manifest, target), binaryName: 'myapp' })
7730
7783
 
7731
7784
  // Checking: bounded, at most once per interval, silent on any failure.
7732
7785
  const check = await checkCliUpdate({ manifestUrl, currentVersion, lastCheckedAt })
@@ -7772,6 +7825,28 @@ One unconvertible schema no longer takes the connection down with it. The tool
7772
7825
  is skipped and **named** (`onSkippedTool`, or a stderr line by default), so a
7773
7826
  surface of two hundred tools is not lost to one.
7774
7827
 
7828
+ **A remote refusal keeps its code.** A failed `tools/call` used to become a
7829
+ one-sentence `Error` with the result discarded, which cost three things at once:
7830
+ the code (so `exitCodes` had nothing to map and every remote failure exited `1`),
7831
+ the message the operator needed, and the error's own class — a plain `Error` is
7832
+ an *unexpected* error to the runner, so it printed a code frame of the framework
7833
+ bundle before the JSON failure and was then scrubbed to `INTERNAL_SERVER_ERROR`.
7834
+ A structured `{ error, details }` body is now relayed as the contract error it
7835
+ is, on every transport; anything else fails as `UPSTREAM_TOOL_ERROR` carrying
7836
+ what the server did send. Not `INTERNAL_SERVER_ERROR`, because nothing of ours
7837
+ broke.
7838
+
7839
+ **On the CLI a discovered command prints the answer, not the envelope.**
7840
+ `tools/call` returns `{ content: [...], structuredContent? }`, and an agent mount
7841
+ needs exactly that — the parts are what a model is shown. The CLI is different in
7842
+ kind, because the handler's value is what gets printed, piped and aggregated:
7843
+ handed the envelope, `--count-by status` groups the *content parts* and answers
7844
+ `no record carries the field "status" — available: text, type`. So the CLI
7845
+ transport unwraps, and only it: `structuredContent` when the server sent one, a
7846
+ lone text part when it parses as JSON, its text when it does not. Several parts,
7847
+ an image or audio pass through whole — picking one of many would be inventing an
7848
+ answer.
7849
+
7775
7850
 
7776
7851
  ## Auth parity
7777
7852
 
@@ -10028,6 +10103,118 @@ onError: (ctx, err) => {
10028
10103
  ```
10029
10104
 
10030
10105
 
10106
+ ==============================================================================
10107
+ # Guide: OAuth & OpenID Connect (docs/guide/oauth.md)
10108
+ ==============================================================================
10109
+
10110
+ # Browser OAuth and Google OIDC
10111
+
10112
+ Stitchkit separates the browser's Authorization Code + PKCE transaction from the server's
10113
+ provider credential verification. The application still owns navigation, users, account matching
10114
+ and merge policy, persistence, sessions, roles, unlink policy and UI.
10115
+
10116
+ ## Supported profile
10117
+
10118
+ The current Google adapter supports one explicit profile: **web OpenID Connect Authorization Code
10119
+ with PKCE**. A browser performs a top-level redirect, an application backend exchanges the
10120
+ one-time code, and Stitchkit returns a verified identity. It is the redirect-based sign-in path;
10121
+ it is not a generic name for every Google identity or authorization mechanism.
10122
+
10123
+ Keep adjacent Google capabilities separate because their credentials, lifecycle and trust
10124
+ boundaries differ:
10125
+
10126
+ | Capability | Credential/result | Stitchkit support |
10127
+ | --- | --- | --- |
10128
+ | Web OIDC Authorization Code + PKCE | verified user identity | `stitchkit/oauth` + `stitchkit/google` |
10129
+ | Google Identity Services button, One Tap or FedCM | browser-delivered ID credential | not implemented |
10130
+ | Incremental authorization for Drive, Calendar or other Google APIs | access/refresh tokens and granted scopes | not implemented |
10131
+ | Installed Android, iOS or desktop application | platform client and system-browser callback | not implemented |
10132
+ | Limited-input/device authorization | device/user codes and polling lifecycle | not implemented |
10133
+ | Service account or workload identity | application identity, possibly domain-wide delegation | not implemented |
10134
+
10135
+ Do not add these as mode flags to `GoogleOidcClient`. Each future capability gets its own adapter
10136
+ and result type, while the provider-neutral PKCE transaction can be reused where its protocol
10137
+ actually applies. In particular, authentication establishes the person; authorization to Google
10138
+ APIs is requested later, in product context, and owns refresh-token persistence and revocation.
10139
+
10140
+ ## Browser transaction
10141
+
10142
+ `stitchkit/oauth` is browser-safe and provider-neutral. It creates independent 32-byte `state`,
10143
+ `nonce` and PKCE verifier values, writes one versioned transaction to caller-provided storage and
10144
+ returns the authorization URL without navigating:
10145
+
10146
+ ```ts
10147
+ import { createAuthorizationCodeClient, safeInternalReturnPath } from 'stitchkit/oauth'
10148
+ import { z } from 'zod'
10149
+
10150
+ const oauth = createAuthorizationCodeClient({
10151
+ authorizationEndpoint: 'https://accounts.google.com/o/oauth2/v2/auth',
10152
+ clientId,
10153
+ redirectUri: `${window.location.origin}/auth/google/callback`,
10154
+ scopes: ['openid', 'email', 'profile'],
10155
+ storage: window.sessionStorage,
10156
+ storageKey: 'app:google:oauth',
10157
+ contextSchema: z.object({ mode: z.enum(['login', 'link']), returnTo: z.string() }),
10158
+ authorizationParameters: { prompt: 'select_account' },
10159
+ })
10160
+
10161
+ const { authorizationUrl } = await oauth.begin({
10162
+ context: { mode: 'login', returnTo: '/account' },
10163
+ })
10164
+ window.location.assign(authorizationUrl)
10165
+
10166
+ // In the callback route. Reading is one-shot even when validation fails.
10167
+ const pending = oauth.consume({ state: new URL(location.href).searchParams.get('state') ?? '' })
10168
+ const returnTo = safeInternalReturnPath(pending.context.returnTo, '/')
10169
+ ```
10170
+
10171
+ Provider parameters cannot replace protocol-owned fields. `consume` removes the pending value
10172
+ before parsing, version checking, context validation or state comparison, so a malformed callback
10173
+ and a React Strict Mode replay cannot reuse it. Errors expose a stable
10174
+ `AuthorizationCodeClientError.code` and never include transaction contents.
10175
+
10176
+ Token exchange, ID-token verification, credential persistence and application identity do not
10177
+ belong to this browser entrypoint.
10178
+
10179
+ ## Google server adapter
10180
+
10181
+ Install the optional peer only in an application that imports `stitchkit/google`:
10182
+
10183
+ ```bash
10184
+ bun add google-auth-library
10185
+ ```
10186
+
10187
+ ```ts
10188
+ import { createGoogleOidcClient } from 'stitchkit/google'
10189
+
10190
+ const google = createGoogleOidcClient({
10191
+ clientId: env.GOOGLE_CLIENT_ID,
10192
+ clientSecret: env.GOOGLE_CLIENT_SECRET,
10193
+ allowedRedirectUris: [env.GOOGLE_WEB_CALLBACK, env.GOOGLE_LOOPBACK_CALLBACK],
10194
+ timeoutMs: 10_000,
10195
+ })
10196
+
10197
+ const identity = await google.exchangeAuthorizationCode({
10198
+ code,
10199
+ codeVerifier: pending.codeVerifier,
10200
+ redirectUri: pending.redirectUri,
10201
+ nonce: pending.nonce,
10202
+ })
10203
+ // { subject, email, name?, picture? }
10204
+ ```
10205
+
10206
+ The redirect must match the immutable allowlist exactly before any outbound call. The token
10207
+ exchange is bounded by an abort deadline; `google-auth-library` verifies the ID-token signature,
10208
+ issuer, audience and expiry. Stitchkit additionally requires `sub`, a valid verified email and an
10209
+ exact nonce. Access, refresh and raw ID tokens never leave the adapter.
10210
+
10211
+ `GoogleOidcError.code` distinguishes `MISCONFIGURED`, `INVALID_CREDENTIAL` and
10212
+ `UPSTREAM_UNAVAILABLE` with fixed safe messages. Endpoints compose their own rate limiter and then
10213
+ map the verified identity into application-owned user/session policy. Offline access, Google API
10214
+ scopes, database writes, user lookup or merging, session rotation, roles and UI remain outside the
10215
+ adapter.
10216
+
10217
+
10031
10218
  ==============================================================================
10032
10219
  # Guide: Observability (docs/guide/observability.md)
10033
10220
  ==============================================================================
@@ -17093,6 +17280,35 @@ adapter is used.
17093
17280
 
17094
17281
  ---
17095
17282
 
17283
+ ## `stitchkit/oauth`
17284
+
17285
+ Browser-safe, provider-neutral Authorization Code + PKCE transaction mechanics. Navigation,
17286
+ token exchange, identity and sessions remain application-owned. See the [OAuth guide](../guide/oauth.md).
17287
+
17288
+ | Export | Kind | Summary |
17289
+ |--------|------|---------|
17290
+ | `createAuthorizationCodeClient` / `AuthorizationCodeClient` / `AuthorizationCodeClientConfig` | function / _type_ | begin one S256 authorization request and consume its versioned pending transaction exactly once |
17291
+ | `BeginAuthorizationCodeInput` / `BeginAuthorizationCodeResult` / `ConsumeAuthorizationCodeInput` / `ConsumedAuthorizationCode` | _type_ | inputs and safe protocol result around caller-validated context |
17292
+ | `AuthorizationCodeStorage` / `AuthorizationCodeCrypto` | _type_ | injected browser capabilities; neither is read at module initialisation |
17293
+ | `AuthorizationCodeClientError` / `AuthorizationCodeClientErrorCode` | class / _type_ | fixed safe failure codes for configuration, context, storage, transaction and state failures |
17294
+ | `safeInternalReturnPath` | function | accept only a single-slash current-origin path without backslashes or ASCII controls |
17295
+
17296
+ ---
17297
+
17298
+ ## `stitchkit/google`
17299
+
17300
+ Server-only Google code exchange and verified OIDC identity. This entry requires the optional
17301
+ `google-auth-library` peer. See the [OAuth guide](../guide/oauth.md).
17302
+
17303
+ | Export | Kind | Summary |
17304
+ |--------|------|---------|
17305
+ | `createGoogleOidcClient` / `GoogleOidcClient` / `GoogleOidcClientConfig` | function / _type_ | exact-redirect, deadline-bounded code exchange and verified identity projection |
17306
+ | `ExchangeGoogleAuthorizationCodeInput` / `GoogleOidcIdentity` / `GoogleOidcClaims` | _type_ | callback input, provider-neutral result and verifier seam claims |
17307
+ | `GoogleOidcExchangeInput` / `GoogleOidcTokenTransport` / `GoogleOidcIdTokenVerifier` | _type_ | injectable transport and verifier boundaries used by production and deterministic tests |
17308
+ | `GoogleOidcError` / `GoogleOidcErrorCode` | class / _type_ | safe `MISCONFIGURED`, `INVALID_CREDENTIAL` or retryable `UPSTREAM_UNAVAILABLE` failure |
17309
+
17310
+ ---
17311
+
17096
17312
  ## `stitchkit/declaration`
17097
17313
 
17098
17314
  Zod-only, dependency-free. The **project declaration**: the single
@@ -17224,11 +17440,11 @@ SDK nor the `ai` peer.
17224
17440
  | `CliBuildAssetSchema` / `CliBuildAsset` | schema / _type_ | one download; `size` and `sha256` describe the **decompressed** bytes |
17225
17441
  | `CliBuildTargetSchema` / `CliBuildTarget` | schema / _type_ | `{ platform, arch }` |
17226
17442
  | `CliBuildStampSchema` / `CliBuildStamp` | schema / _type_ | the version/commit/build time carried inside a binary |
17227
- | `CliInstallerConfig` | _type_ | manifest, asset, binary name and default install directory |
17443
+ | `CliInstallerConfig` | _type_ | manifest, binary name, default install directory, and an optional `asset` — omit it for one script that selects the target by `uname` |
17228
17444
  | `CliUpdateCheckConfig` / `CliUpdateCheck` | _type_ | check inputs, and its four answers — `skipped`, `current`, `outdated`, `unknown` |
17229
17445
  | `CliUpdateApplyConfig` / `AppliedCliUpdate` | _type_ | apply inputs and the replaced path, byte count and digest |
17230
17446
  | `CliProfileStore` / `CliProfileStoreConfig` / `ResolvedCliProfile` | _type_ | the profile store, its directory/schema/hint config, and one resolution |
17231
- | `CliResultView` | _type_ | the requested aggregate — `count`, `sum` or `table` |
17447
+ | `CliResultView` | _type_ | the requested view — `count`, `sum`, or `records` (ordered and/or table-rendered) |
17232
17448
  | `CliViewOutput` | _type_ | a JSON value, or the one human-facing text shape |
17233
17449
  | `CliGlobalOptionsParse` | _type_ | `{ argv, globals }` returned by `extractCliGlobalOptions` |
17234
17450
  | `CliArgvRoute` | _type_ | `{ command, commandArgv, topLevelHelp, version, error? }` returned by `routeCliArgv` |
package/llms.txt CHANGED
@@ -21,6 +21,7 @@ Build with stitchkit: define a contract once, then `implement` it and serve it (
21
21
  - [Realtime](https://github.com/max-listov/stitchkit/blob/master/docs/guide/realtime.md): Socket.IO server/client wrappers, handshake auth, the cache bridge, a raw WebSocket lane
22
22
  - [Live data](https://github.com/max-listov/stitchkit/blob/master/docs/guide/live.md): defineEvents beside the contract, watched reads shared by every subscriber, keyspaces with authoritative memory, and the trust fence
23
23
  - [Auth & errors](https://github.com/max-listov/stitchkit/blob/master/docs/guide/auth-and-errors.md): scopes, createAuthHook, JWT/cookies, the AppError model, the stitch error-code registry
24
+ - [OAuth & OpenID Connect](https://github.com/max-listov/stitchkit/blob/master/docs/guide/oauth.md): browser Authorization Code + PKCE transactions and the optional Google OIDC server adapter
24
25
  - [Observability](https://github.com/max-listov/stitchkit/blob/master/docs/guide/observability.md): request and tool-call observability, W3C trace context, createObservability
25
26
  - [Testing & deployment](https://github.com/max-listov/stitchkit/blob/master/docs/guide/testing-and-deployment.md): in-process testing; deploying on Bun and on Node (serveNode)
26
27
  - [Multi-tenant](https://github.com/max-listov/stitchkit/blob/master/docs/guide/multi-tenant.md): a /tenants/:id/… scenario end-to-end — scopePrefixes, scoped client, extend
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stitchkit",
3
- "version": "0.90.4",
3
+ "version": "0.90.6",
4
4
  "description": "Contract-first backend framework — one defineContract() into an HTTP API, MCP tools, AI-agent tools and a typed client. Bun and Node.",
5
5
  "keywords": [
6
6
  "bun",
@@ -185,6 +185,14 @@
185
185
  "types": "./dist/geo.d.ts",
186
186
  "import": "./dist/geo.js"
187
187
  },
188
+ "./oauth": {
189
+ "types": "./dist/oauth.d.ts",
190
+ "import": "./dist/oauth.js"
191
+ },
192
+ "./google": {
193
+ "types": "./dist/google.d.ts",
194
+ "import": "./dist/google.js"
195
+ },
188
196
  "./declaration": {
189
197
  "types": "./dist/declaration.d.ts",
190
198
  "import": "./dist/declaration.js"
@@ -202,7 +210,8 @@
202
210
  "check": "bun x tsc --noEmit",
203
211
  "build:js": "bun run build:entrypoints && bun scripts/preserve-webpack-ignore.mjs",
204
212
  "build:types": "bun x tsc -p tsconfig.build.json --emitDeclarationOnly && bun scripts/rewrite-declaration-specifiers.mjs",
205
- "build": "rm -rf dist && cp ../../CHANGELOG.md ./CHANGELOG.md && bun run build:js && chmod +x dist/upgrade-cli.js && bun run build:types && bun scripts/check-browser-clean.mjs && bun scripts/check-browser-executes.mjs && bun scripts/check-env-live.mjs && bun scripts/check-public-types.mjs && bun scripts/check-declarations-strict.mjs",
213
+ "build": "bun ../../scripts/package-build-lock.ts -- bun run build:unlocked",
214
+ "build:unlocked": "rm -rf dist && cp ../../CHANGELOG.md ./CHANGELOG.md && bun run build:js && chmod +x dist/upgrade-cli.js && bun run build:types && bun scripts/check-browser-clean.mjs && bun scripts/check-browser-executes.mjs && bun scripts/check-env-live.mjs && bun scripts/check-public-types.mjs && bun scripts/check-declarations-strict.mjs",
206
215
  "dev": "bun run build:entrypoints -- --watch",
207
216
  "prepack": "cp ../../README.md ./README.md && bun ../../scripts/gen-llms.ts && bun run build",
208
217
  "test": "bun test",
@@ -214,23 +223,24 @@
214
223
  "build:entrypoints": "bun scripts/build-entrypoints.mjs"
215
224
  },
216
225
  "peerDependencies": {
217
- "@modelcontextprotocol/ext-apps": "^1.7.2",
226
+ "@modelcontextprotocol/ext-apps": "^1.7.2 || ^2.0.0",
218
227
  "@modelcontextprotocol/server": "^2.0.0",
219
- "@openrouter/ai-sdk-provider": "^3.0.0",
228
+ "@openrouter/ai-sdk-provider": "^3.1.0",
220
229
  "@opentelemetry/api": "^1.9.0",
221
- "@socket.io/bun-engine": "^0.1.1",
230
+ "@socket.io/bun-engine": "^0.1.2",
222
231
  "@socket.io/component-emitter": "^3.1.2",
223
232
  "@tanstack/react-query": ">=5",
224
- "@types/bun": "^1.3.14",
225
- "ai": "^7.0.0",
226
- "grammy": "^1.45.1",
233
+ "@types/bun": "^1.4.2",
234
+ "ai": "^7.0.107",
235
+ "grammy": "^1.46.0",
236
+ "google-auth-library": "^11.1.0",
227
237
  "maxmind": "^5.0.7",
228
238
  "react": ">=18",
229
239
  "react-query-kit": "^3.3.3",
230
240
  "socket.io": "^4.8.3",
231
241
  "socket.io-client": "^4.8.3",
232
- "srvx": "^0.12.5",
233
- "zod": "^4.4.3"
242
+ "srvx": "^0.12.5 || ^1.0.5",
243
+ "zod": "^4.6.5"
234
244
  },
235
245
  "peerDependenciesMeta": {
236
246
  "@modelcontextprotocol/ext-apps": {
@@ -263,6 +273,9 @@
263
273
  "grammy": {
264
274
  "optional": true
265
275
  },
276
+ "google-auth-library": {
277
+ "optional": true
278
+ },
266
279
  "maxmind": {
267
280
  "optional": true
268
281
  },
@@ -283,30 +296,31 @@
283
296
  }
284
297
  },
285
298
  "dependencies": {
286
- "ky": "^2.0.2"
299
+ "ky": "^2.1.0"
287
300
  },
288
301
  "devDependencies": {
289
302
  "@modelcontextprotocol/client": "^2.0.0",
290
- "@modelcontextprotocol/ext-apps": "^1.7.5",
303
+ "@modelcontextprotocol/ext-apps": "^2.0.0",
291
304
  "@modelcontextprotocol/server": "^2.0.0",
292
- "@openrouter/ai-sdk-provider": "^3.0.0",
305
+ "@openrouter/ai-sdk-provider": "^3.1.0",
293
306
  "@opentelemetry/api": "^1.9.0",
294
- "@socket.io/bun-engine": "^0.1.1",
307
+ "@socket.io/bun-engine": "^0.1.2",
295
308
  "@socket.io/component-emitter": "^3.1.2",
296
- "@tanstack/react-query": "5.102.3",
297
- "@types/bun": "^1.4.0",
309
+ "@tanstack/react-query": "5.103.1",
310
+ "@types/bun": "^1.4.2",
298
311
  "@types/json-schema": "^7.0.15",
299
- "@types/react": "^19.2.18",
312
+ "@types/react": "^19.3.0",
300
313
  "@typescript/typescript6": "^6.0.2",
301
- "ai": "^7.0.93",
314
+ "ai": "^7.0.107",
302
315
  "fake-indexeddb": "^6.2.5",
303
- "grammy": "^1.45.1",
304
- "react": "^19.2.8",
316
+ "grammy": "^1.46.0",
317
+ "google-auth-library": "^11.1.0",
318
+ "react": "^19.3.0",
305
319
  "react-query-kit": "^3.3.4",
306
320
  "socket.io": "^4.8.3",
307
321
  "socket.io-client": "^4.8.3",
308
- "srvx": "^0.12.7",
322
+ "srvx": "^1.0.5",
309
323
  "typescript": "^7.0.2",
310
- "zod": "^4.4.3"
324
+ "zod": "^4.6.5"
311
325
  }
312
326
  }
@@ -18,13 +18,13 @@ import {
18
18
  probeAgentProcessSandbox,
19
19
  refuseMissingCodingPath
20
20
  } from "./index-gte38nbm.js";
21
+ import {
22
+ defineRuntimeTool
23
+ } from "./index-8z9we758.js";
21
24
  import {
22
25
  conflict,
23
26
  forbidden
24
27
  } from "./index-scs3f1eg.js";
25
- import {
26
- defineRuntimeTool
27
- } from "./index-8z9we758.js";
28
28
 
29
29
  // src/agent-runtime/coding-tool-contract.ts
30
30
  import { z } from "zod";
@@ -7,20 +7,20 @@ import {
7
7
  normalizeError,
8
8
  validateDeclaredOutput
9
9
  } from "./index-2hryh65w.js";
10
+ import {
11
+ coerceJsonArgs
12
+ } from "./index-7zbps32p.js";
13
+ import {
14
+ objectShapeKeys
15
+ } from "./index-fenaekmk.js";
10
16
  import {
11
17
  AppError,
12
18
  STITCH_ERROR_STATUS,
13
19
  isStitchErrorCode
14
20
  } from "./index-scs3f1eg.js";
15
- import {
16
- coerceJsonArgs
17
- } from "./index-7zbps32p.js";
18
21
  import {
19
22
  isUnsafeKey
20
23
  } from "./index-kzfs85xp.js";
21
- import {
22
- objectShapeKeys
23
- } from "./index-fenaekmk.js";
24
24
  import {
25
25
  isRecord
26
26
  } from "./index-77fekveh.js";