@irogane/kaji 0.2.0-beta.11
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/LICENSE +105 -0
- package/README.md +598 -0
- package/contracts/README.md +14 -0
- package/contracts/beta-core-v1.json +52 -0
- package/contracts/cli/init-cases-v1.json +27 -0
- package/contracts/errors/error-codes.json +48 -0
- package/contracts/errors/integration-recovery-v1.json +127 -0
- package/contracts/errors/provider-normalization.json +111 -0
- package/contracts/events/conformance-invalid.json +20 -0
- package/contracts/events/conformance.json +511 -0
- package/contracts/events/new-kaji-event-v1.schema.json +1021 -0
- package/contracts/events/stored-kaji-event-v1.schema.json +1025 -0
- package/contracts/feature-tiers-v1.json +488 -0
- package/contracts/integrations/abi-index-v1.json +8 -0
- package/contracts/integrations/conformance-invalid.json +443 -0
- package/contracts/integrations/conformance-valid.json +121 -0
- package/contracts/integrations/copy-provenance-v1.schema.json +61 -0
- package/contracts/integrations/echo-tool-abi-v1.json +37 -0
- package/contracts/integrations/github-api-conformance-v1.json +644 -0
- package/contracts/integrations/github-tool-abi-typescript-v1.json +369 -0
- package/contracts/integrations/github-tool-abi-v1.json +146 -0
- package/contracts/integrations/gmail-api-conformance-v1.json +750 -0
- package/contracts/integrations/gmail-tool-abi-v1.json +62 -0
- package/contracts/integrations/index.schema.json +37 -0
- package/contracts/integrations/manifest.schema.json +119 -0
- package/contracts/parity/expected-normalized.json +4906 -0
- package/contracts/parity/scenarios.json +100 -0
- package/contracts/parity/scenarios.schema.json +214 -0
- package/contracts/providers/cost-conformance.json +112 -0
- package/contracts/release/github-proof-v1.schema.json +138 -0
- package/contracts/release/gmail-proof-v1.schema.json +138 -0
- package/contracts/release/kaji-ts-consumer-handoff-v1.schema.json +1289 -0
- package/contracts/release/publisher-identity-receipt-v1.schema.json +319 -0
- package/contracts/release/typescript-onboarding-evidence-v1.schema.json +740 -0
- package/contracts/tools/conformance-invalid.json +271 -0
- package/contracts/tools/conformance-valid.json +78 -0
- package/contracts/tools/tool-schema-v1.schema.json +18 -0
- package/dist/anthropic.cjs +1231 -0
- package/dist/anthropic.cjs.map +1 -0
- package/dist/anthropic.d.cts +26 -0
- package/dist/anthropic.d.ts +26 -0
- package/dist/anthropic.js +270 -0
- package/dist/anthropic.js.map +1 -0
- package/dist/auth.cjs +1507 -0
- package/dist/auth.cjs.map +1 -0
- package/dist/auth.d.cts +129 -0
- package/dist/auth.d.ts +129 -0
- package/dist/auth.js +1039 -0
- package/dist/auth.js.map +1 -0
- package/dist/base-B9FRMcP8.d.cts +140 -0
- package/dist/base-nHQd1VtS.d.ts +140 -0
- package/dist/chunk-AAM33KAO.js +4367 -0
- package/dist/chunk-AAM33KAO.js.map +1 -0
- package/dist/chunk-KAJ6BM64.js +153 -0
- package/dist/chunk-KAJ6BM64.js.map +1 -0
- package/dist/chunk-KCAXIOZS.js +308 -0
- package/dist/chunk-KCAXIOZS.js.map +1 -0
- package/dist/chunk-LSJ4AVO2.js +243 -0
- package/dist/chunk-LSJ4AVO2.js.map +1 -0
- package/dist/chunk-TM7ZGOJX.js +716 -0
- package/dist/chunk-TM7ZGOJX.js.map +1 -0
- package/dist/cli/bin.d.ts +2 -0
- package/dist/cli/bin.js +13 -0
- package/dist/cli/bin.js.map +1 -0
- package/dist/cli/chunk-2RCWPRVY.js +6277 -0
- package/dist/cli/chunk-2RCWPRVY.js.map +1 -0
- package/dist/cli/chunk-SEBX54TR.js +681 -0
- package/dist/cli/chunk-SEBX54TR.js.map +1 -0
- package/dist/cli/index.d.ts +232 -0
- package/dist/cli/index.js +11 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init-worker.d.ts +2 -0
- package/dist/cli/init-worker.js +18 -0
- package/dist/cli/init-worker.js.map +1 -0
- package/dist/cli/integration-copy-worker.js +48 -0
- package/dist/cli/integration-copy-worker.js.map +1 -0
- package/dist/cli/package-entry-cjs.cjs +21 -0
- package/dist/cli/package-entry-cjs.cjs.map +1 -0
- package/dist/cli/package-entry-cjs.d.cts +2 -0
- package/dist/cli/package-entry.d.ts +2 -0
- package/dist/cli/package-entry.js +13 -0
- package/dist/cli/package-entry.js.map +1 -0
- package/dist/context-BaFHrQHv.d.cts +21 -0
- package/dist/context-BaFHrQHv.d.ts +21 -0
- package/dist/context-C-YPY-GS.d.cts +1538 -0
- package/dist/context-C-YPY-GS.d.ts +1538 -0
- package/dist/index.cjs +12346 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1560 -0
- package/dist/index.d.ts +1560 -0
- package/dist/index.js +7852 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/github.cjs +2092 -0
- package/dist/integrations/github.cjs.map +1 -0
- package/dist/integrations/github.d.cts +21 -0
- package/dist/integrations/github.d.ts +21 -0
- package/dist/integrations/github.js +2088 -0
- package/dist/integrations/github.js.map +1 -0
- package/dist/integrations.cjs +3370 -0
- package/dist/integrations.cjs.map +1 -0
- package/dist/integrations.d.cts +202 -0
- package/dist/integrations.d.ts +202 -0
- package/dist/integrations.js +2650 -0
- package/dist/integrations.js.map +1 -0
- package/dist/observability-Cj--OkME.d.cts +96 -0
- package/dist/observability-Cj--OkME.d.ts +96 -0
- package/dist/openai.cjs +1235 -0
- package/dist/openai.cjs.map +1 -0
- package/dist/openai.d.cts +32 -0
- package/dist/openai.d.ts +32 -0
- package/dist/openai.js +272 -0
- package/dist/openai.js.map +1 -0
- package/dist/testing.cjs +554 -0
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.d.cts +43 -0
- package/dist/testing.d.ts +43 -0
- package/dist/testing.js +118 -0
- package/dist/testing.js.map +1 -0
- package/package.json +188 -0
- package/registry/echo/index.ts +53 -0
- package/registry/echo/manifest.json +53 -0
- package/registry/github/LICENSE +105 -0
- package/registry/github/client.ts +1727 -0
- package/registry/github/index.ts +263 -0
- package/registry/github/manifest.json +227 -0
- package/registry/github/owner-fixtures.json +10 -0
- package/registry/github/tests/github.test.ts +32 -0
- package/registry/gmail/LICENSE +105 -0
- package/registry/gmail/client.ts +548 -0
- package/registry/gmail/index.ts +165 -0
- package/registry/gmail/manifest.json +102 -0
- package/registry/gmail/owner-fixtures.json +10 -0
- package/registry/gmail/tests/gmail.test.ts +32 -0
- package/registry/index.json +21 -0
- package/registry/index.schema.json +37 -0
- package/registry/schema.json +119 -0
package/README.md
ADDED
|
@@ -0,0 +1,598 @@
|
|
|
1
|
+
# Kaji (TypeScript)
|
|
2
|
+
|
|
3
|
+
`kaji` is an embeddable SDK for building agents in TypeScript: import
|
|
4
|
+
the pieces you need and compose them. The core is infra-free (no database,
|
|
5
|
+
server, or environment configured). It mirrors the runtime core of the Python
|
|
6
|
+
`kaji` SDK.
|
|
7
|
+
|
|
8
|
+
<!-- canonical-status-links:start -->
|
|
9
|
+
> Canonical documentation: https://github.com/enkyuan/alloy/blob/main/docs/kaji/README.md
|
|
10
|
+
> Release status and evidence: https://github.com/enkyuan/alloy/blob/main/kaji/RELEASE_MATRIX.md
|
|
11
|
+
<!-- canonical-status-links:end -->
|
|
12
|
+
|
|
13
|
+
OpenAI is the sole beta-supported primary provider. Anthropic, Gemini, Kimi,
|
|
14
|
+
and OpenRouter are opt-in experimental/WIP adapters with no beta compatibility
|
|
15
|
+
or publication-proof commitment. Use `MockProvider` as the deterministic
|
|
16
|
+
local/test default.
|
|
17
|
+
|
|
18
|
+
See [**Kaji MVP**](https://github.com/enkyuan/alloy/blob/main/docs/MVP.md) for
|
|
19
|
+
the full five-step developer path and scope definition.
|
|
20
|
+
|
|
21
|
+
## Install
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install @irogane/kaji@0.2.0-beta.11 zod openai # OpenAI
|
|
25
|
+
# or
|
|
26
|
+
npm install @irogane/kaji@0.2.0-beta.11 zod @anthropic-ai/sdk # Anthropic (experimental/WIP)
|
|
27
|
+
# or: bun add @irogane/kaji@0.2.0-beta.11 zod openai
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
`zod` is a required peer dependency (Zod 4). `openai` and `@anthropic-ai/sdk`
|
|
31
|
+
are optional peers -- install only the one you use. Anthropic is not in the
|
|
32
|
+
beta support tier. The package metadata declares Node `22.x || 24.x`, but the
|
|
33
|
+
protected beta onboarding evidence is deliberately narrower: it proves npm and
|
|
34
|
+
Bun on GitHub-hosted Linux/x64, with Node 22 on `ubuntu-22.04` and Node 24 on
|
|
35
|
+
`ubuntu-24.04`. It makes no broader runtime or platform claim, including for
|
|
36
|
+
macOS/arm64, Windows, or fully offline dependency installation. The protected
|
|
37
|
+
compiler cells use TypeScript 5.7.3 and 6.0.3.
|
|
38
|
+
|
|
39
|
+
## Quick start
|
|
40
|
+
|
|
41
|
+
First prove a text-only turn without credentials. No principal is required
|
|
42
|
+
because this runtime has no enabled tools. Save this example as
|
|
43
|
+
`quickstart.mts` so top-level await runs as ESM, including from npm's default
|
|
44
|
+
CommonJS project:
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import { AgentBuilder } from "@irogane/kaji";
|
|
48
|
+
import { MockProvider } from "@irogane/kaji/testing";
|
|
49
|
+
|
|
50
|
+
const runtime = new AgentBuilder().provider(new MockProvider({ reply: "hello" })).build();
|
|
51
|
+
const result = await runtime.turn("Say hello.");
|
|
52
|
+
console.log(result.text, result.accounting);
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Privileged event journal and disposal
|
|
56
|
+
|
|
57
|
+
> **Security boundary:** `TurnResult.events` and `AgentRuntime.history()` are a
|
|
58
|
+
> privileged full-fidelity journal. They can contain user prompts,
|
|
59
|
+
> provider-derived text and deltas, tool arguments, tool results, and arbitrary
|
|
60
|
+
> metadata. They are not redaction-safe; never log, attach, or export them
|
|
61
|
+
> wholesale.
|
|
62
|
+
|
|
63
|
+
`MetricsSink` and `TraceSink` are best-effort timing and correlation surfaces,
|
|
64
|
+
not complete business or audit records. Sink failures are swallowed so
|
|
65
|
+
observability cannot change turn behavior. Traces still contain access-controlled
|
|
66
|
+
correlation identifiers even though they omit full event payloads.
|
|
67
|
+
|
|
68
|
+
Failed turns throw: failed turns have no `TurnResult` and therefore no
|
|
69
|
+
`TurnResult.events` or successful-turn `TurnAccounting` aggregate. Applications
|
|
70
|
+
that need failure evidence must choose a preselected session ID, retain the
|
|
71
|
+
caught error separately for live control flow, page history with an
|
|
72
|
+
exclusive `afterSequence` cursor until an empty page, and reduce to an allowlist
|
|
73
|
+
before export; generic provider failures have no durable recovery code today. Use the
|
|
74
|
+
caught typed provider error and `normalizeProviderError()` where applicable.
|
|
75
|
+
Always page until an empty page; a short page is not proof of exhaustion.
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
import type { AgentRuntime, StoredKajiEvent } from "@irogane/kaji";
|
|
79
|
+
|
|
80
|
+
async function pageHistory(runtime: AgentRuntime, sessionId: string, limit = 128) {
|
|
81
|
+
const events: StoredKajiEvent[] = [];
|
|
82
|
+
let afterSequence = 0;
|
|
83
|
+
for (;;) {
|
|
84
|
+
const page = await runtime.history(sessionId, { afterSequence, limit });
|
|
85
|
+
if (page.length === 0) return events;
|
|
86
|
+
const nextSequence = page.at(-1)!.sequence;
|
|
87
|
+
if (nextSequence <= afterSequence) throw new Error("history cursor did not advance");
|
|
88
|
+
events.push(...page);
|
|
89
|
+
afterSequence = nextSequence;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const SAFE_FIELDS = [
|
|
94
|
+
"tool_name",
|
|
95
|
+
"tool_call_id",
|
|
96
|
+
"error_code",
|
|
97
|
+
"phase",
|
|
98
|
+
"retryable",
|
|
99
|
+
"outcome",
|
|
100
|
+
"reason_code",
|
|
101
|
+
"recovery_code",
|
|
102
|
+
"doc_url",
|
|
103
|
+
] as const;
|
|
104
|
+
|
|
105
|
+
function safeJournalEvidence(event: StoredKajiEvent): Record<string, unknown> {
|
|
106
|
+
const safe: Record<string, unknown> = { sequence: event.sequence, type: event.type };
|
|
107
|
+
if (event.turn_id !== undefined) safe.turn_id = event.turn_id;
|
|
108
|
+
for (const field of SAFE_FIELDS) {
|
|
109
|
+
const value = Reflect.get(event, field);
|
|
110
|
+
if (value !== undefined) safe[field] = value;
|
|
111
|
+
}
|
|
112
|
+
return safe; // never content, delta, tool_args, result, metadata, or raw session_id
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const sessionId = crypto.randomUUID();
|
|
116
|
+
let failure: { error: unknown } | undefined;
|
|
117
|
+
try {
|
|
118
|
+
await runtime.turn("Investigate the failure.", { sessionId });
|
|
119
|
+
} catch (error) {
|
|
120
|
+
failure = { error }; // retain separately; never add this value to safe evidence
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (failure !== undefined) {
|
|
124
|
+
stopIngress(sessionId);
|
|
125
|
+
await runtime.drainTools(10_000);
|
|
126
|
+
await runtime.drainProviders(10_000);
|
|
127
|
+
try {
|
|
128
|
+
const privilegedHistory = await pageHistory(runtime, sessionId);
|
|
129
|
+
const exportableEvidence = privilegedHistory.map(safeJournalEvidence);
|
|
130
|
+
sendToYourIncidentStore(exportableEvidence);
|
|
131
|
+
} catch (evidenceError) {
|
|
132
|
+
handleEvidenceExportError(evidenceError); // report separately; original failure stays authoritative
|
|
133
|
+
} finally {
|
|
134
|
+
await runtime.purgeSession(sessionId);
|
|
135
|
+
}
|
|
136
|
+
handleOriginalError(failure.error); // cleanup finished; preserve original control flow
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Before disposal, stop ingress for the named session so another caller cannot
|
|
141
|
+
race the drain-to-purge interval. On a live runtime, drain tools and providers,
|
|
142
|
+
page and reduce any required evidence, then call `purgeSession(sessionId)` while
|
|
143
|
+
leaving other sessions running. For whole-runtime shutdown, `close()` may run
|
|
144
|
+
first to reject future turn APIs; history and purge remain callable afterward.
|
|
145
|
+
`close()` does not delete retained history and does not cancel already-active
|
|
146
|
+
work.
|
|
147
|
+
|
|
148
|
+
This lifecycle is identical to Python's supported in-memory path. The bounded
|
|
149
|
+
store never evicts a retained session implicitly: a full store raises
|
|
150
|
+
`EventStoreCapacityError` until the host explicitly purges one. Runtime purge
|
|
151
|
+
closes its old subscribers, removes the event and ID indexes, and clears every
|
|
152
|
+
runtime owner sharing the store; a standalone raw listener must be closed by
|
|
153
|
+
its caller. Reset cursors to `0` before reuse; the next generation begins at
|
|
154
|
+
sequence `1`.
|
|
155
|
+
|
|
156
|
+
`PurgeableEventStore.purgeSession(sessionId)` is the public one-argument
|
|
157
|
+
store-only capability, detected by `supportsSessionPurge()`. Runtime purge also
|
|
158
|
+
requires Kaji's internal opaque coordinated capability, so a custom store that
|
|
159
|
+
implements only the public method fails closed at the runtime boundary. Direct
|
|
160
|
+
store operations and new owners remain fenced throughout purge. Split delivery
|
|
161
|
+
is unsupported because its outbox cannot cross a reused generation.
|
|
162
|
+
|
|
163
|
+
TypeScript's embedded defaults are bounded, in-memory, and process-local. This
|
|
164
|
+
beta ships no persistent event store or distributed coordinator and does not
|
|
165
|
+
release-certify host implementations; durability, deletion, and cross-process
|
|
166
|
+
correctness are host responsibilities. `purgeSession()` removes SDK-owned
|
|
167
|
+
retained indexes and caches but cannot promise VM string zeroization or erase
|
|
168
|
+
copies already sent to logs, sinks, providers, custom stores, crash dumps, or
|
|
169
|
+
caller-owned objects.
|
|
170
|
+
|
|
171
|
+
A custom `ToolIdempotencyLedger` must implement optional `releaseSettled()` to
|
|
172
|
+
participate in purge. The event store and SDK caches are already cleared before
|
|
173
|
+
host ledger cleanup is awaited. If that cleanup rejects, deletion cannot be
|
|
174
|
+
rolled back; repair the host ledger and retry the named purge. If it never
|
|
175
|
+
settles, the strong `cleanup_pending` tombstone keeps turns, direct store
|
|
176
|
+
operations, subscriptions, and new owners fenced. A later runtime purge retries
|
|
177
|
+
cleanup without repeating physical deletion. Kaji cannot force hostile
|
|
178
|
+
in-process host code to settle. `TurnAccounting` remains TypeScript-only and is
|
|
179
|
+
separate from the cross-SDK purge contract.
|
|
180
|
+
|
|
181
|
+
Then set an API key and add a risk-classified tool with explicit caller
|
|
182
|
+
identity, deadline, and cancellation:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
export OPENAI_API_KEY=sk-...
|
|
186
|
+
# Experimental/WIP alternative: export ANTHROPIC_API_KEY=sk-ant-...
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
```ts
|
|
190
|
+
import { AgentBuilder, OpenAIProvider, Integration, deadlineAfter, tool } from "@irogane/kaji";
|
|
191
|
+
import { z } from "zod";
|
|
192
|
+
|
|
193
|
+
class WeatherIntegration extends Integration {
|
|
194
|
+
readonly namespace = "weather";
|
|
195
|
+
|
|
196
|
+
readonly getWeather = tool(
|
|
197
|
+
{
|
|
198
|
+
description: "Return weather for a city.",
|
|
199
|
+
parameters: z.object({ city: z.string() }),
|
|
200
|
+
risk: "read",
|
|
201
|
+
},
|
|
202
|
+
async (args, _context) => ({ city: args.city, tempF: 68 }),
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const runtime = new AgentBuilder()
|
|
207
|
+
.provider(new OpenAIProvider({ apiKey: process.env.OPENAI_API_KEY! }))
|
|
208
|
+
.integration(new WeatherIntegration())
|
|
209
|
+
.systemPrompt("You are a weather assistant.")
|
|
210
|
+
.build();
|
|
211
|
+
|
|
212
|
+
const result = await runtime.turn("Weather in Seattle?", {
|
|
213
|
+
context: {
|
|
214
|
+
principalId: "weather-app",
|
|
215
|
+
deadlineAtMs: deadlineAfter(30_000),
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
console.log(result.text, result.accounting);
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Swap `OpenAIProvider` for `AnthropicProvider` (and `OPENAI_API_KEY` for
|
|
222
|
+
`ANTHROPIC_API_KEY`) to use the experimental/WIP Anthropic adapter.
|
|
223
|
+
|
|
224
|
+
`AgentBuilder` wires a scoped `ToolRegistry` into `ToolPlanner` so integration
|
|
225
|
+
tools are both visible to the model and executable.
|
|
226
|
+
|
|
227
|
+
### GitHub integration
|
|
228
|
+
|
|
229
|
+
The experimental TypeScript package subpath exposes a fixed-origin GitHub
|
|
230
|
+
integration without copying its source into your application. For an
|
|
231
|
+
investigation agent, opt into read-only exposure so the two mutation tools are
|
|
232
|
+
not registered or sent to the model.
|
|
233
|
+
|
|
234
|
+
```ts
|
|
235
|
+
import { AgentBuilder, OpenAIProvider, deadlineAfter } from "@irogane/kaji";
|
|
236
|
+
import { createGithubIntegration } from "@irogane/kaji/integrations/github";
|
|
237
|
+
|
|
238
|
+
const principalId = "github-investigator";
|
|
239
|
+
const github = createGithubIntegration({
|
|
240
|
+
repositories: ["owner/repo"],
|
|
241
|
+
toolExposure: "read-only",
|
|
242
|
+
tokenFor: async (context) => {
|
|
243
|
+
if (context.signal.aborted) throw context.signal.reason;
|
|
244
|
+
if (context.principalId !== principalId) throw new Error("GitHub credential unavailable");
|
|
245
|
+
const token = process.env.GITHUB_TOKEN;
|
|
246
|
+
if (!token) throw new Error("GITHUB_TOKEN is required");
|
|
247
|
+
return token;
|
|
248
|
+
},
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
const runtime = new AgentBuilder()
|
|
252
|
+
.provider(new OpenAIProvider({ apiKey: process.env.OPENAI_API_KEY! }))
|
|
253
|
+
.integration(github)
|
|
254
|
+
.defaultContext({ principalId })
|
|
255
|
+
.systemPrompt("Use GitHub evidence from owner/repo and cite immutable refs.")
|
|
256
|
+
.build();
|
|
257
|
+
|
|
258
|
+
const result = await runtime.turn("Inspect the latest failed checks.", {
|
|
259
|
+
context: { deadlineAtMs: deadlineAfter(30_000) },
|
|
260
|
+
});
|
|
261
|
+
console.log(result.text);
|
|
262
|
+
github.close();
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
`toolExposure` controls which tools reach the model; it does not reduce token
|
|
266
|
+
permissions. Keep the repository allowlist narrow and use a fine-grained token
|
|
267
|
+
with only the read permissions needed by the selected tools. The callback is
|
|
268
|
+
lazy, receives the tool execution context, and returns the raw token; Kaji
|
|
269
|
+
validates it and adds the `Bearer` header. `AgentBuilder` does not own the
|
|
270
|
+
integration, so close it only after active tool work has settled. See the
|
|
271
|
+
[GitHub integration guide](https://github.com/enkyuan/alloy/blob/main/apps/docs/content/integrations/github.mdx)
|
|
272
|
+
for the 15-tool catalog, mutation policy, limits, and unsupported surfaces.
|
|
273
|
+
|
|
274
|
+
### Gmail integration (experimental)
|
|
275
|
+
|
|
276
|
+
Gmail ships as an **experimental** catalog entry. Unlike `github`, it has no
|
|
277
|
+
`@irogane/kaji/integrations/gmail` package subpath. You copy its source into your
|
|
278
|
+
project and own it:
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
npm exec -- kaji add gmail --allow-experimental --out ./integrations/gmail
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
The copied `index.ts` exports `createGmailIntegration({ tokenFor })`, where
|
|
285
|
+
`tokenFor` is a lazy callback that returns the OAuth access token for the
|
|
286
|
+
`gmail.readonly` and `gmail.send` scopes. Wire it into `AgentBuilder` exactly
|
|
287
|
+
like the GitHub integration:
|
|
288
|
+
|
|
289
|
+
```ts
|
|
290
|
+
import { createGmailIntegration } from "./integrations/gmail";
|
|
291
|
+
|
|
292
|
+
const gmail = createGmailIntegration({
|
|
293
|
+
tokenFor: async (context) => {
|
|
294
|
+
if (context.signal.aborted) throw context.signal.reason;
|
|
295
|
+
const token = process.env.GMAIL_ACCESS_TOKEN;
|
|
296
|
+
if (!token) throw new Error("GMAIL_ACCESS_TOKEN is required");
|
|
297
|
+
return token;
|
|
298
|
+
},
|
|
299
|
+
});
|
|
300
|
+
// ...builder.integration(gmail)... then gmail.close() after tool work settles.
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
It exposes three tools: `list_messages` (with `page_token`/`next_page_token`
|
|
304
|
+
pagination), `get_message` (first `text/plain` body, decoded and bounded), and
|
|
305
|
+
`send_message`, an external-effect write whose ambiguous failures surface the
|
|
306
|
+
`gmail_mutation_unknown` recovery reason rather than risking a silent double
|
|
307
|
+
send. The client is fixed to `https://gmail.googleapis.com`, rejects redirects,
|
|
308
|
+
and bounds every response. See the
|
|
309
|
+
[Gmail integration guide](https://github.com/enkyuan/alloy/blob/main/apps/docs/content/integrations/index.mdx)
|
|
310
|
+
for scopes, the `kaji connect gmail` grant flow, and limits.
|
|
311
|
+
|
|
312
|
+
`deadlineAtMs` is an absolute Unix epoch value; use `deadlineAfter()` when the
|
|
313
|
+
caller has a duration. An earlier caller deadline can tighten, but never extend,
|
|
314
|
+
the configured 120-second whole-turn default covering queue wait, provider open
|
|
315
|
+
and streaming, approval, and tool work. Cooperative provider shutdown may use
|
|
316
|
+
the additional configured cancellation grace.
|
|
317
|
+
|
|
318
|
+
Catch a Kaji `ProviderError`, then call `normalizeProviderError(error)` for the
|
|
319
|
+
redaction-safe `type`, `code`, `service`, `action`, `status`, and `retryable`
|
|
320
|
+
fields. The normalizer accepts Kaji provider errors, not arbitrary vendor
|
|
321
|
+
exceptions.
|
|
322
|
+
|
|
323
|
+
See [`docs/kaji/production-beta.md`](https://github.com/enkyuan/alloy/blob/main/docs/kaji/production-beta.md) for
|
|
324
|
+
the installed-package version of both first-success examples and exact default
|
|
325
|
+
limits. Operating details are in
|
|
326
|
+
[`concurrency-and-ordering.md`](https://github.com/enkyuan/alloy/blob/main/docs/kaji/concurrency-and-ordering.md),
|
|
327
|
+
[`tool-contracts.md`](https://github.com/enkyuan/alloy/blob/main/docs/kaji/tool-contracts.md), and
|
|
328
|
+
[`troubleshooting.md`](https://github.com/enkyuan/alloy/blob/main/docs/kaji/troubleshooting.md).
|
|
329
|
+
Call `runtime.effectiveLimits()` to inspect the immutable
|
|
330
|
+
`EffectiveRuntimeLimits` resolved for one runtime.
|
|
331
|
+
|
|
332
|
+
Runtimes that share the same `EventStore` also share a default per-store turn
|
|
333
|
+
coordinator within the current process, so same-session turns serialize even
|
|
334
|
+
when separate builders create the runtimes. Different stores do not block one
|
|
335
|
+
another. This is not a distributed lock: multi-process deployments must inject
|
|
336
|
+
a `SessionTurnCoordinator` backed by shared infrastructure.
|
|
337
|
+
|
|
338
|
+
## Prove it with a model
|
|
339
|
+
|
|
340
|
+
OpenAI with `gpt-5.4-mini` is the recommended first live check because it is
|
|
341
|
+
cost-effective and exercises the SDK's Chat Completions tool path.
|
|
342
|
+
|
|
343
|
+
```bash
|
|
344
|
+
bun --filter kaji test:quickstart
|
|
345
|
+
OPENAI_API_KEY=... KAJI_LIVE_OPENAI_MODEL=gpt-5.4-mini \
|
|
346
|
+
bun --filter kaji test:integration tests/integration/openai-tools.test.ts
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
The live test registers a read-only probe tool, verifies the model calls it,
|
|
350
|
+
and verifies the runtime emits final assistant text using the tool result.
|
|
351
|
+
Keyed OpenAI proof requires protected tool loops in both SDKs on one exact
|
|
352
|
+
commit. A missing `OPENAI_API_KEY` blocks that release evidence. Anthropic,
|
|
353
|
+
Kimi, Gemini, and OpenRouter are experimental/WIP; the latter three are
|
|
354
|
+
OpenAI-compatible factories rather than native provider implementations.
|
|
355
|
+
|
|
356
|
+
For the cross-SDK release gate, run from the repository root:
|
|
357
|
+
|
|
358
|
+
```bash
|
|
359
|
+
uv run --project kaji/packages/py python kaji/scripts/beta_release_check.py
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
This wraps Python unit/static checks, Python wheel smoke, TS unit/static/build
|
|
363
|
+
checks, TS package smoke, mandatory pinned ast-grep boundary checks, and no-key
|
|
364
|
+
live-gate hygiene. The ast-grep step guards the Python SDK/service boundary, core package dependency direction, removed tool-model imports, TypeScript optional provider imports, and cancellation error shape.
|
|
365
|
+
|
|
366
|
+
For the live-gate credential modes specifically:
|
|
367
|
+
|
|
368
|
+
```bash
|
|
369
|
+
uv run --project kaji/packages/py python kaji/scripts/verify_openai_loop.py
|
|
370
|
+
KAJI_REQUIRE_LIVE_KEYS=1 uv run --project kaji/packages/py python kaji/scripts/verify_openai_loop.py
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
Without `OPENAI_API_KEY`, the first command proves missing-key hygiene only.
|
|
374
|
+
It is not provider evidence. The protected release mode requires
|
|
375
|
+
`OPENAI_API_KEY` and fails when it is absent.
|
|
376
|
+
|
|
377
|
+
```bash
|
|
378
|
+
OPENAI_API_KEY=... KAJI_LIVE_OPENAI_MODEL=gpt-5.4-mini uv run --project kaji/packages/py python kaji/scripts/verify_openai_loop.py
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
`KAJI_RUN_KEYED_LIVE=1` is the fail-closed two-cell OpenAI proof (Python and
|
|
382
|
+
TypeScript). It requires the OpenAI key, frozen artifact set, and exact
|
|
383
|
+
40-character release commit:
|
|
384
|
+
|
|
385
|
+
```bash
|
|
386
|
+
OPENAI_API_KEY=... \
|
|
387
|
+
KAJI_RELEASE_ARTIFACTS_DIR="$PWD/.artifacts/kaji-release" \
|
|
388
|
+
KAJI_RELEASE_COMMIT=<40-character-commit> KAJI_RUN_KEYED_LIVE=1 \
|
|
389
|
+
uv run --project kaji/packages/py python kaji/scripts/beta_release_check.py
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
The protected rehearsal and publish workflows are authoritative release
|
|
393
|
+
evidence. Their required-reviewer environments are purpose-specific:
|
|
394
|
+
`kaji-onboarding` protects deterministic TypeScript onboarding,
|
|
395
|
+
`kaji-release` protects keyed OpenAI proof, and `kaji-publish` protects
|
|
396
|
+
publisher identity and the sole npm write. The single-provider command above
|
|
397
|
+
is only a local paid smoke test.
|
|
398
|
+
|
|
399
|
+
## Stability tiers
|
|
400
|
+
|
|
401
|
+
- **Stable core:** `AgentBuilder`, `AgentRuntime`, `ToolRegistry`,
|
|
402
|
+
`ToolPlanner`, session replay, the OpenAI provider, and the in-memory
|
|
403
|
+
event store/committer form the supported embedded-agent surface.
|
|
404
|
+
- **Experimental providers:** Anthropic, Gemini, Kimi, and OpenRouter remain
|
|
405
|
+
opt-in WIP surfaces without a beta compatibility or publication-proof
|
|
406
|
+
commitment.
|
|
407
|
+
- **Experimental Python-only:** Redis realtime/history, voice/TTS,
|
|
408
|
+
`DocumentRAG`, native Gemini/Kimi providers, tool retrieval, and text/voice
|
|
409
|
+
modalities exist in Python but are not production-hardened.
|
|
410
|
+
- **TS not ported:** Redis realtime, voice/TTS, and RAG are not implemented in
|
|
411
|
+
TypeScript. TS Gemini/Kimi remain OpenAI-compatible factories rather than
|
|
412
|
+
native provider implementations.
|
|
413
|
+
|
|
414
|
+
See https://github.com/enkyuan/alloy/blob/main/kaji/RELEASE_MATRIX.md for the
|
|
415
|
+
cross-SDK release matrix and the exact distinction between stable core,
|
|
416
|
+
experimental Python-only surfaces, and TypeScript surfaces that are not ported.
|
|
417
|
+
|
|
418
|
+
The beta promise is the core agent loop. Redis realtime/history, voice/TTS,
|
|
419
|
+
`DocumentRAG`, native Gemini/Kimi, and tool retrieval remain outside the beta
|
|
420
|
+
gate until the promotion criteria in `kaji/RELEASE_MATRIX.md` are met.
|
|
421
|
+
Gemini and Kimi are OpenAI-compatible factories in TypeScript, not native
|
|
422
|
+
provider implementations.
|
|
423
|
+
|
|
424
|
+
## Approval handler
|
|
425
|
+
|
|
426
|
+
Tools whose risk exceeds your policy threshold pause for approval before the
|
|
427
|
+
runtime executes them. All hosts use `TypedApprovalHandler` and return an
|
|
428
|
+
`ApprovalDecision`. `cliApprovalHandler` is a typed dev/REPL implementation
|
|
429
|
+
that prints the tool name, risk, and arguments, then reads `y` / `N` on stdin:
|
|
430
|
+
|
|
431
|
+
```ts
|
|
432
|
+
import { AgentBuilder, cliApprovalHandler, openai } from "@irogane/kaji";
|
|
433
|
+
|
|
434
|
+
const agent = new AgentBuilder()
|
|
435
|
+
.provider(openai())
|
|
436
|
+
.approvalHandler(cliApprovalHandler({ label: "agent-a" }))
|
|
437
|
+
.build();
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
Custom hosts implement `TypedApprovalHandler.request(call, context)` and return
|
|
441
|
+
an `ApprovalDecision`, for example
|
|
442
|
+
`{ granted: true, code: "approved" }` or a rejected decision with an explicit
|
|
443
|
+
code and safe reason. See
|
|
444
|
+
[`tool-contracts.md`](https://github.com/enkyuan/alloy/blob/main/docs/kaji/tool-contracts.md) for the lifecycle.
|
|
445
|
+
|
|
446
|
+
`EventApprovalHandler` requires a non-empty turn ID and accepts a decision only
|
|
447
|
+
when `turn_id`, `tool_call_id`, and `tool_name` all match the pending request.
|
|
448
|
+
Unscoped or stale backlog decisions are ignored.
|
|
449
|
+
|
|
450
|
+
## CLI
|
|
451
|
+
|
|
452
|
+
```
|
|
453
|
+
kaji --help # list subcommands
|
|
454
|
+
kaji add <integration> # copy an integration into your project
|
|
455
|
+
kaji add <integration> --allow-experimental # explicitly copy a non-beta template
|
|
456
|
+
kaji init [path] --provider mock --yes # no-key TypeScript scaffold
|
|
457
|
+
kaji list-integrations # enumerate the registry catalog
|
|
458
|
+
kaji replay <session.jsonl> # render a stored JSONL session log
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
This is the embedded `kaji` CLI. The standalone cross-language
|
|
462
|
+
`@irogane/kaji/cli` scaffold has its own `--lang`/`--provider` options; Python's `kaji`
|
|
463
|
+
package also exposes additional Python-only maintenance commands.
|
|
464
|
+
Generated projects pin dotenvx and load `.env` from their `start` script after
|
|
465
|
+
you copy `.env.example` to `.env`.
|
|
466
|
+
|
|
467
|
+
`echo` and `github` are beta catalog entries. `--allow-experimental` is
|
|
468
|
+
required only for catalog entries still marked experimental.
|
|
469
|
+
|
|
470
|
+
## Global tool registry (advanced)
|
|
471
|
+
|
|
472
|
+
For simple setups you can use the process-level registry:
|
|
473
|
+
|
|
474
|
+
```ts
|
|
475
|
+
import { executeTool, registerTool, toolSpecFromSchema } from "@irogane/kaji";
|
|
476
|
+
import { z } from "zod";
|
|
477
|
+
|
|
478
|
+
registerTool(
|
|
479
|
+
toolSpecFromSchema("get_weather", "Look up weather", z.object({ city: z.string() }), "read"),
|
|
480
|
+
async (args, context) => ({
|
|
481
|
+
principalId: context.principalId,
|
|
482
|
+
city: args.city,
|
|
483
|
+
tempF: 68,
|
|
484
|
+
}),
|
|
485
|
+
);
|
|
486
|
+
|
|
487
|
+
const result = await executeTool(
|
|
488
|
+
"get_weather",
|
|
489
|
+
{ city: "Seattle" },
|
|
490
|
+
{
|
|
491
|
+
principalId: "user-1",
|
|
492
|
+
sessionId: "session-1",
|
|
493
|
+
turnId: "turn-1",
|
|
494
|
+
requestId: "request-1",
|
|
495
|
+
traceId: "trace-1",
|
|
496
|
+
toolCallId: "call-1",
|
|
497
|
+
idempotencyKey: "session-1:call-1",
|
|
498
|
+
signal: new AbortController().signal,
|
|
499
|
+
metadata: {},
|
|
500
|
+
},
|
|
501
|
+
);
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
## What's exported
|
|
505
|
+
|
|
506
|
+
| Export | What it is |
|
|
507
|
+
| ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
508
|
+
| `EventType`, `KajiEvent` | Event discriminants and Zod-validated event union |
|
|
509
|
+
| `EventStore`, `InMemoryEventStore` | Event log that is append-only while retained; explicit session purge is an optional lifecycle capability |
|
|
510
|
+
| `EventBus` | In-memory pub/sub per session |
|
|
511
|
+
| `replaySession`, `SessionManager`, session store types | Session projection and management |
|
|
512
|
+
| `registerTool`, `ToolRegistry`, `toolSpecFromSchema`, `executeTool`, `listToolSpecs` | Tool registry (global + scoped) |
|
|
513
|
+
| `ToolPolicy`, `ToolPlanner` | Allow/deny and approval-gated execution |
|
|
514
|
+
| `TypedApprovalHandler`, `cliApprovalHandler`, `EventApprovalHandler`, `AutoApprovalHandler` | Structured approval handlers: stdin, event-driven (publishes `TOOL_APPROVAL_REQUESTED` for a host UI to answer), and auto-decide by policy |
|
|
515
|
+
| `OpenAIProvider`, `AnthropicProvider` | LLM providers; Anthropic is experimental/WIP |
|
|
516
|
+
| `normalizeProviderError`, `NormalizedProviderError` | Redaction-safe semantic classification for Kaji provider errors |
|
|
517
|
+
| `openai`, `anthropic`, `kimi`, `gemini`, `openrouter` | One-line provider factories; only OpenAI is beta-supported |
|
|
518
|
+
| `getProvider`, `registerProvider` | Name-keyed provider registry, for host code that resolves a provider by config string |
|
|
519
|
+
| `generateText`, `streamText` | One-shot provider calls without a full `AgentRuntime`: a single request/response or stream, no event log |
|
|
520
|
+
| `AgentRuntime`, `AgentBuilder`, `CancellationToken` | ReAct loop and fluent builder |
|
|
521
|
+
| `EffectiveRuntimeLimits` | Immutable values returned by `AgentRuntime.effectiveLimits()` after overrides |
|
|
522
|
+
| `Integration`, `tool` | Integration helper for scoped tools |
|
|
523
|
+
| `EnvSecretSource` | Reads a named secret from `process.env`; the default `SecretSource` implementation |
|
|
524
|
+
|
|
525
|
+
Events use snake_case field names (`session_id`, `tool_name`) as the wire format
|
|
526
|
+
shared with the Python SDK.
|
|
527
|
+
|
|
528
|
+
## Python vs TypeScript parity
|
|
529
|
+
|
|
530
|
+
| Feature | Python SDK | TS SDK |
|
|
531
|
+
| ------------------------------------- | ---------------------- | --------------------------------------------------- |
|
|
532
|
+
| Event-sourced runtime | Yes | Yes |
|
|
533
|
+
| Tool registry + planner + policy | Yes | Yes |
|
|
534
|
+
| `AgentBuilder` + integrations | Yes | Yes |
|
|
535
|
+
| OpenAI provider | Yes | Yes |
|
|
536
|
+
| Anthropic provider (experimental/WIP) | Yes | Yes |
|
|
537
|
+
| Kimi / Gemini providers | Yes (experimental/WIP) | Yes (experimental/WIP, OpenAI-compatible factories) |
|
|
538
|
+
| Document RAG / vector store | Yes (non-MVP) | No |
|
|
539
|
+
| Tool retriever | Yes (non-MVP) | No |
|
|
540
|
+
| Text modality adapter | Yes (non-MVP) | No |
|
|
541
|
+
| Voice / TTS | Yes (non-MVP) | No |
|
|
542
|
+
| Redis realtime bus | Yes (non-MVP) | No (in-memory only) |
|
|
543
|
+
| CLI scaffold | Yes | Yes |
|
|
544
|
+
|
|
545
|
+
## Testing without API keys
|
|
546
|
+
|
|
547
|
+
Unit and integration tests mock the provider HTTP client -- no keys needed for
|
|
548
|
+
the default test suite:
|
|
549
|
+
|
|
550
|
+
```bash
|
|
551
|
+
bun run test
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
Live provider tests are opt-in and skip automatically when keys are absent.
|
|
555
|
+
OpenAI is the beta-supported live path; the Anthropic command remains a WIP
|
|
556
|
+
adapter check:
|
|
557
|
+
|
|
558
|
+
```bash
|
|
559
|
+
OPENAI_API_KEY=... bun run test:integration
|
|
560
|
+
OPENAI_API_KEY=... KAJI_LIVE_OPENAI_MODEL=gpt-5.4-mini \
|
|
561
|
+
bun run test:integration tests/integration/openai-tools.test.ts
|
|
562
|
+
ANTHROPIC_API_KEY=... bun run test:integration
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
`MockProvider` is a deterministic stub that exercises the full tool loop. It is
|
|
566
|
+
available from `@irogane/kaji/testing` for unit tests, not from the main package
|
|
567
|
+
entrypoint used to build real agents.
|
|
568
|
+
|
|
569
|
+
```ts
|
|
570
|
+
import { MockProvider } from "@irogane/kaji/testing";
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
## Development
|
|
574
|
+
|
|
575
|
+
```bash
|
|
576
|
+
# from the repository root
|
|
577
|
+
bun install
|
|
578
|
+
bun --filter kaji typecheck
|
|
579
|
+
bun --filter kaji format:check
|
|
580
|
+
bun --filter kaji test
|
|
581
|
+
bun --filter kaji build
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
## Relation to the Python SDK
|
|
585
|
+
|
|
586
|
+
This package ports the **runtime core** of the Python `kaji` SDK: events,
|
|
587
|
+
sessions, tools, providers, and the ReAct loop. Python's Redis realtime bus,
|
|
588
|
+
RAG, and text/voice modalities are not yet ported. The Python bus can be
|
|
589
|
+
Redis-backed for multi-process deployments; the TS `EventBus` is in-memory only.
|
|
590
|
+
|
|
591
|
+
## License
|
|
592
|
+
|
|
593
|
+
Kaji is source-available under the
|
|
594
|
+
[Functional Source License 1.1, ALv2 Future License](https://spdx.org/licenses/FSL-1.1-ALv2.html).
|
|
595
|
+
It permits internal commercial use, modification, and redistribution for
|
|
596
|
+
permitted purposes, but excludes competing commercial products and services;
|
|
597
|
+
each version becomes Apache-2.0 after two years. FSL is not an OSI-approved
|
|
598
|
+
open-source license.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Kaji beta contracts
|
|
2
|
+
|
|
3
|
+
These files define the behavior shared by the Python and TypeScript SDK beta.
|
|
4
|
+
|
|
5
|
+
- `beta-core-v1.json` pins public defaults and stability boundaries.
|
|
6
|
+
- `feature-tiers-v1.json` is the machine-readable stable/experimental surface.
|
|
7
|
+
- `events/` separates event drafts from sequenced stored events.
|
|
8
|
+
- `tools/` contains the tool-spec schema and shared validation fixtures.
|
|
9
|
+
- `errors/error-codes.json` is the normalized public failure vocabulary;
|
|
10
|
+
`errors/provider-normalization.json` pins cross-SDK status classification.
|
|
11
|
+
|
|
12
|
+
Canonical files live in `kaji/contracts`. Package copies are generated and
|
|
13
|
+
checked by `kaji/scripts/sync_beta_contracts.py`; do not edit package copies
|
|
14
|
+
directly.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://kaji.dev/contracts/beta-core-v1.json",
|
|
4
|
+
"contractVersion": "1.0.0",
|
|
5
|
+
"runtime": {
|
|
6
|
+
"sameSessionTurns": "serialized",
|
|
7
|
+
"crossSessionTurns": "concurrent",
|
|
8
|
+
"maxToolIterations": 5,
|
|
9
|
+
"contextWindowTurns": 32,
|
|
10
|
+
"contextWindowCharacters": 100000,
|
|
11
|
+
"turnTimeoutMs": 120000,
|
|
12
|
+
"providerCancellationGraceMs": 5000,
|
|
13
|
+
"providerTextMaxBytes": 262144,
|
|
14
|
+
"providerToolArgumentsMaxBytes": 65536,
|
|
15
|
+
"providerResponseMaxBytes": 524288,
|
|
16
|
+
"providerToolCallsMax": 64
|
|
17
|
+
},
|
|
18
|
+
"tools": {
|
|
19
|
+
"schemaDraft": "2020-12",
|
|
20
|
+
"validateFormats": true,
|
|
21
|
+
"maxConcurrency": 4,
|
|
22
|
+
"timeoutMs": 30000,
|
|
23
|
+
"approvalTimeoutMs": 300000,
|
|
24
|
+
"riskRequired": true,
|
|
25
|
+
"idempotencyKey": "session_id:tool_call_id",
|
|
26
|
+
"idempotencyMaxEntries": 10000,
|
|
27
|
+
"idempotencyCompletedTtlSeconds": 86400
|
|
28
|
+
},
|
|
29
|
+
"events": {
|
|
30
|
+
"ordering": "store_sequence",
|
|
31
|
+
"draftSchema": "new-kaji-event-v1.schema.json",
|
|
32
|
+
"storedSchema": "stored-kaji-event-v1.schema.json",
|
|
33
|
+
"warmProjection": "cursor_suffix",
|
|
34
|
+
"subscriberQueueCapacity": 1024,
|
|
35
|
+
"maxDurableToolArgumentBytes": 65536,
|
|
36
|
+
"maxDurableToolResultBytes": 65536,
|
|
37
|
+
"maxDurableEventBytes": 1048576,
|
|
38
|
+
"inMemoryStoreMaxSessions": 1000,
|
|
39
|
+
"inMemoryStoreMaxEventsPerSession": 10000,
|
|
40
|
+
"inMemorySessionAdmission": "fail_closed_until_explicit_purge",
|
|
41
|
+
"purgedSessionReuse": "fresh_sequence",
|
|
42
|
+
"purgeClosesExistingSubscribers": true,
|
|
43
|
+
"purgeFencesDirectStoreOperations": true,
|
|
44
|
+
"postDeleteCleanup": "tombstone_until_converged",
|
|
45
|
+
"splitDeliveryPurge": "unsupported",
|
|
46
|
+
"overflow": "typed_error"
|
|
47
|
+
},
|
|
48
|
+
"integrations": {
|
|
49
|
+
"stable": ["echo", "github", "gmail"],
|
|
50
|
+
"experimentalRequiresOptIn": true
|
|
51
|
+
}
|
|
52
|
+
}
|