From 4254eb1cae754fa6e84eefbd4f780c757d38ca4b Mon Sep 17 00:00:00 2001 From: "upstash-tag[bot]" <313023939+upstash-tag[bot]@users.noreply.github.com> Date: Tue, 8 Sep 2026 08:26:21 +0000 Subject: [PATCH 1/4] fix(eve-extension): allow zero-arg agentkit() mount to typecheck eve types ExtensionHandle's call signature with a required argument even though the extension's config schema is fully optional, so the README's documented 'export default agentkit();' failed tsc with TS2554 while 'agentkit({})' worked. Re-type the default export with an optional config parameter (eve's runtime already treats the argument as optional) so the documented zero-arg form typechecks, matching the README's claim that every field is optional. No runtime behavior change; adds a regression test and a changeset. --- .../eve-extension-optional-mount-config.md | 22 +++ CLAUDE.md | 11 ++ packages/eve-extension/AGENTS.md | 4 + packages/eve-extension/extension/extension.ts | 126 ++++++++++-------- .../eve-extension/test/mount-config.test.ts | 30 +++++ 5 files changed, 139 insertions(+), 54 deletions(-) create mode 100644 .changeset/eve-extension-optional-mount-config.md create mode 100644 packages/eve-extension/test/mount-config.test.ts diff --git a/.changeset/eve-extension-optional-mount-config.md b/.changeset/eve-extension-optional-mount-config.md new file mode 100644 index 0000000..6c18aed --- /dev/null +++ b/.changeset/eve-extension-optional-mount-config.md @@ -0,0 +1,22 @@ +--- +"@upstash/agentkit-eve-extension": patch +--- + +fix: make the mount config argument optional, so the documented `agentkit()` mount typechecks + +The README's smallest mount — `export default agentkit();` — failed `tsc` with +`TS2554: Expected 1 arguments, but got 0`, even though every field of the config schema is +optional. eve types `ExtensionHandle`'s call signature as `(values: InferInput)`, a +*required* parameter, regardless of how optional the schema is, and a required parameter +can't be omitted in TypeScript even when its type admits `undefined`. `eve build` doesn't +typecheck the mount file, so this only bit consumers running `tsc` or reading the error in +their editor; `agentkit({})` was the (undocumented) workaround. + +The default export is now typed with the config parameter optional +(`(config?: AgentkitConfig) => MountedExtension`), keeping eve's `config`/`schema` members. +Nothing changes at runtime — eve's `defineExtension` already validates `values ?? {}`, so a +zero-argument mount was always fine at runtime, and field-level type checking of a passed +config is unchanged. Guarded by a new `test/mount-config.test.ts`, which the package's +`typecheck` script also compiles. Verified end to end: a real eve app mounting +`export default agentkit();` typechecks and `eve build`s with all seven tools plus the +chat-history hook contributed. diff --git a/CLAUDE.md b/CLAUDE.md index 3da2a1c..bdf3253 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -147,6 +147,17 @@ and `eve-extension-demo` (a minimal eve scaffold that mounts the extension). (in eve's `dist/src/compiler/extension-compatibility.js`) supports them all, and move the peer floor to match.** - `extension/extension.ts` = `defineExtension({ config: zod })`; the default export is the mount factory. + The default export is **cast to a local `AgentkitExtension` type whose config parameter is optional** + (`(config?: AgentkitConfig) => MountedExtension`, keeping eve's `config`/`schema` members via + `Pick, …>`): eve types `ExtensionHandle`'s call signature as `(values: InferInput)`, + a *required* parameter no matter how optional the schema is, and TS won't let a required parameter be + omitted even when its type admits `undefined` — so the README's smallest mount `agentkit()` failed with + `TS2554: Expected 1 arguments, but got 0` (issue: `eve build` doesn't typecheck the mount file, so only + `tsc`/editor consumers saw it). Runtime was always fine — `defineExtension` validates `values ?? {}`. + Don't drop the cast when editing the schema, and don't try to fix it with `.optional()`/`.default({})` + on the schema: that only widens the *type* of the argument, not its optionality. + Guarded by `test/mount-config.test.ts` (a zero-arg mount + a `@ts-expect-error` bad field) — the + package's `typecheck` covers `test/`, so removing the cast fails `pnpm typecheck`, not just review. Config knobs: `userId` (string or `(ctx: SessionContext) => string` — eve's public base of tool+hook ctx, imported from `eve/tools`), `redis` (defaults `Redis.fromEnv()`), `memory{topK,minScore}`, `search{schema,indexName,prefix,defaultLimit}`, `chatHistory: boolean | {prefix,indexName,ttlSeconds}` diff --git a/packages/eve-extension/AGENTS.md b/packages/eve-extension/AGENTS.md index c4a5e07..d69262f 100644 --- a/packages/eve-extension/AGENTS.md +++ b/packages/eve-extension/AGENTS.md @@ -15,6 +15,10 @@ unavailable, use https://eve.dev/docs/extensions as a fallback. - Declare the extension in `extension/extension.ts` with `defineExtension` from `eve/extension`. Config is optional; read bound values via the handle's `.config` in tools and hooks. + Because every config field here is optional, the default export is cast to a + type whose config **parameter** is optional too — eve's `ExtensionHandle` call + signature requires the argument, which made the documented `agentkit()` mount + fail `tsc` with TS2554. Keep that cast if you touch the schema. - Add contributions under `extension/` the same way as in an agent: `tools/`, `channels/`, `connections/`, `skills/`, `schedules/`, `subagents/`, `hooks/`, and optional instruction fragments (eve ≥0.41 supports the full set; diff --git a/packages/eve-extension/extension/extension.ts b/packages/eve-extension/extension/extension.ts index db06459..37f2e6f 100644 --- a/packages/eve-extension/extension/extension.ts +++ b/packages/eve-extension/extension/extension.ts @@ -1,4 +1,4 @@ -import { defineExtension } from "eve/extension"; +import { defineExtension, type ExtensionHandle, type MountedExtension } from "eve/extension"; import type { SessionContext } from "eve/tools"; import { z } from "zod"; import type { Redis } from "@upstash/redis"; @@ -16,58 +16,76 @@ const userId = z.union([ z.custom<(ctx: SessionContext) => string>((value) => typeof value === "function"), ]); -export default defineExtension({ - config: z.object({ - userId: userId.optional(), - /** Upstash Redis client. Defaults to `Redis.fromEnv()` (`UPSTASH_REDIS_REST_URL`/`_TOKEN`). */ - redis: z.custom((value) => typeof value === "object" && value !== null).optional(), - /** - * Report the sdk name + version to Upstash as a header on the requests made by the redis client. - * Can also be disabled with the `UPSTASH_DISABLE_TELEMETRY` env var. Defaults to `true`. - */ - enableTelemetry: z.boolean().optional(), - /** Knobs for the `recall_memory` tool. */ - memory: z - .object({ - /** Max memories returned by a recall. */ - topK: z.number().int().positive().optional(), - /** Minimum BM25 relevance score for recall hits. */ - minScore: z.number().optional(), - }) - .optional(), - /** - * Enables the `search` / `aggregate` / `count` tools over one Upstash Redis Search index. Without - * this, those tools error at call time — configure it, or disable their slots with `disableTool()`. - */ - search: z - .object({ - /** The index schema, built with `s` from `@upstash/redis`. */ - schema: z.custom((value) => typeof value === "object" && value !== null), - /** Index name. Defaults to `"agentkit:search"`. */ - indexName: z.string().min(1).optional(), - /** Key prefix for indexed JSON documents. Defaults to `":"`. */ +const configSchema = z.object({ + userId: userId.optional(), + /** Upstash Redis client. Defaults to `Redis.fromEnv()` (`UPSTASH_REDIS_REST_URL`/`_TOKEN`). */ + redis: z.custom((value) => typeof value === "object" && value !== null).optional(), + /** + * Report the sdk name + version to Upstash as a header on the requests made by the redis client. + * Can also be disabled with the `UPSTASH_DISABLE_TELEMETRY` env var. Defaults to `true`. + */ + enableTelemetry: z.boolean().optional(), + /** Knobs for the `recall_memory` tool. */ + memory: z + .object({ + /** Max memories returned by a recall. */ + topK: z.number().int().positive().optional(), + /** Minimum BM25 relevance score for recall hits. */ + minScore: z.number().optional(), + }) + .optional(), + /** + * Enables the `search` / `aggregate` / `count` tools over one Upstash Redis Search index. Without + * this, those tools error at call time — configure it, or disable their slots with `disableTool()`. + */ + search: z + .object({ + /** The index schema, built with `s` from `@upstash/redis`. */ + schema: z.custom((value) => typeof value === "object" && value !== null), + /** Index name. Defaults to `"agentkit:search"`. */ + indexName: z.string().min(1).optional(), + /** Key prefix for indexed JSON documents. Defaults to `":"`. */ + prefix: z.string().min(1).optional(), + /** Default page size for the `search` tool. Defaults to 10. */ + defaultLimit: z.number().int().positive().optional(), + }) + .optional(), + /** + * Durable transcript capture into Upstash Redis `ChatHistory` (**off by default**): a hook + * appends every user and assistant message as it streams, keyed by `userId` + session id. Pass + * `true` to enable it with defaults, or an object to enable it and tune where chats are stored. + */ + chatHistory: z + .union([ + z.boolean(), + z.object({ + /** Base key prefix for stored chats; defaults to `agentkit:chat`. */ prefix: z.string().min(1).optional(), - /** Default page size for the `search` tool. Defaults to 10. */ - defaultLimit: z.number().int().positive().optional(), - }) - .optional(), - /** - * Durable transcript capture into Upstash Redis `ChatHistory` (**off by default**): a hook - * appends every user and assistant message as it streams, keyed by `userId` + session id. Pass - * `true` to enable it with defaults, or an object to enable it and tune where chats are stored. - */ - chatHistory: z - .union([ - z.boolean(), - z.object({ - /** Base key prefix for stored chats; defaults to `agentkit:chat`. */ - prefix: z.string().min(1).optional(), - /** Redis Search index name. Defaults to the (identifier-safe) `prefix`. */ - indexName: z.string().min(1).optional(), - /** Optional TTL (seconds) per chat. Omit for no expiry. */ - ttlSeconds: z.number().int().positive().optional(), - }), - ]) - .optional(), - }), + /** Redis Search index name. Defaults to the (identifier-safe) `prefix`. */ + indexName: z.string().min(1).optional(), + /** Optional TTL (seconds) per chat. Omit for no expiry. */ + ttlSeconds: z.number().int().positive().optional(), + }), + ]) + .optional(), }); + +/** The mount config. Every field is optional, so the whole object may be omitted entirely. */ +type AgentkitConfig = z.input; + +/** + * The mount factory this package default-exports, with the config argument made **optional**. + * + * eve types `ExtensionHandle`'s call signature with a required `values` argument even when every + * field of the config schema is optional, so a bare `agentkit()` fails `tsc` with TS2554 (`eve build` + * doesn't typecheck, so it only bites consumers in an editor / `tsc`). eve's runtime already accepts + * the omitted argument — `defineExtension` validates `values ?? {}` — so this only widens the type to + * match the documented API: the smallest mount is `agentkit()`. + */ +type HandleMembers = Pick, "config" | "schema">; + +interface AgentkitExtension extends HandleMembers { + (config?: AgentkitConfig): MountedExtension; +} + +export default defineExtension({ config: configSchema }) as AgentkitExtension; diff --git a/packages/eve-extension/test/mount-config.test.ts b/packages/eve-extension/test/mount-config.test.ts new file mode 100644 index 0000000..8c8cb16 --- /dev/null +++ b/packages/eve-extension/test/mount-config.test.ts @@ -0,0 +1,30 @@ +import { describe, expect, test } from "vitest"; +import agentkit from "../extension/extension"; + +/** + * Every config field is optional, so the README's smallest mount is a bare `agentkit()`. eve types + * `ExtensionHandle`'s call signature with a *required* argument regardless, which made that mount + * fail `tsc` with TS2554, so `extension.ts` re-types the default export with an optional parameter. + * This file guards both halves of that: the zero-argument call has to compile (the package's + * `typecheck` script covers `test/`) and still produce a mounted extension, and a config that *is* + * passed has to keep being validated field by field. + */ +const MOUNTED_EXTENSION = Symbol.for("eve.mounted-extension"); + +describe("mount factory", () => { + test("mounts with no config at all", () => { + const mounted = agentkit(); + + expect(Object.getOwnPropertySymbols(mounted)).toContain(MOUNTED_EXTENSION); + expect(agentkit.config).toEqual({}); + }); + + test("mounts with an empty config object", () => { + expect(Object.getOwnPropertySymbols(agentkit({}))).toContain(MOUNTED_EXTENSION); + }); + + test("still validates the fields it is given", () => { + // @ts-expect-error — an optional parameter must not weaken per-field type checking + expect(() => agentkit({ memory: { topK: "nope" } })).toThrow(/Invalid extension config/); + }); +}); From dbe35c9b158a746a464050db10feff1bd3ddb545 Mon Sep 17 00:00:00 2001 From: "upstash-tag[bot]" <313023939+upstash-tag[bot]@users.noreply.github.com> Date: Tue, 8 Sep 2026 12:09:08 +0000 Subject: [PATCH 2/4] Revert "fix(eve-extension): allow zero-arg agentkit() mount to typecheck" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 4254eb1cae754fa6e84eefbd4f780c757d38ca4b. Reverted on review: re-typing the default export to widen the mount factory's config parameter (plus its regression test, changeset and guide notes) is a large diff for a documentation-level problem. The next commit applies the smaller fix — the README example uses agentkit({}), which is what the exported types accept. Co-Authored-By: Claude Opus 5 --- .../eve-extension-optional-mount-config.md | 22 --- CLAUDE.md | 11 -- packages/eve-extension/AGENTS.md | 4 - packages/eve-extension/extension/extension.ts | 126 ++++++++---------- .../eve-extension/test/mount-config.test.ts | 30 ----- 5 files changed, 54 insertions(+), 139 deletions(-) delete mode 100644 .changeset/eve-extension-optional-mount-config.md delete mode 100644 packages/eve-extension/test/mount-config.test.ts diff --git a/.changeset/eve-extension-optional-mount-config.md b/.changeset/eve-extension-optional-mount-config.md deleted file mode 100644 index 6c18aed..0000000 --- a/.changeset/eve-extension-optional-mount-config.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -"@upstash/agentkit-eve-extension": patch ---- - -fix: make the mount config argument optional, so the documented `agentkit()` mount typechecks - -The README's smallest mount — `export default agentkit();` — failed `tsc` with -`TS2554: Expected 1 arguments, but got 0`, even though every field of the config schema is -optional. eve types `ExtensionHandle`'s call signature as `(values: InferInput)`, a -*required* parameter, regardless of how optional the schema is, and a required parameter -can't be omitted in TypeScript even when its type admits `undefined`. `eve build` doesn't -typecheck the mount file, so this only bit consumers running `tsc` or reading the error in -their editor; `agentkit({})` was the (undocumented) workaround. - -The default export is now typed with the config parameter optional -(`(config?: AgentkitConfig) => MountedExtension`), keeping eve's `config`/`schema` members. -Nothing changes at runtime — eve's `defineExtension` already validates `values ?? {}`, so a -zero-argument mount was always fine at runtime, and field-level type checking of a passed -config is unchanged. Guarded by a new `test/mount-config.test.ts`, which the package's -`typecheck` script also compiles. Verified end to end: a real eve app mounting -`export default agentkit();` typechecks and `eve build`s with all seven tools plus the -chat-history hook contributed. diff --git a/CLAUDE.md b/CLAUDE.md index bdf3253..3da2a1c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -147,17 +147,6 @@ and `eve-extension-demo` (a minimal eve scaffold that mounts the extension). (in eve's `dist/src/compiler/extension-compatibility.js`) supports them all, and move the peer floor to match.** - `extension/extension.ts` = `defineExtension({ config: zod })`; the default export is the mount factory. - The default export is **cast to a local `AgentkitExtension` type whose config parameter is optional** - (`(config?: AgentkitConfig) => MountedExtension`, keeping eve's `config`/`schema` members via - `Pick, …>`): eve types `ExtensionHandle`'s call signature as `(values: InferInput)`, - a *required* parameter no matter how optional the schema is, and TS won't let a required parameter be - omitted even when its type admits `undefined` — so the README's smallest mount `agentkit()` failed with - `TS2554: Expected 1 arguments, but got 0` (issue: `eve build` doesn't typecheck the mount file, so only - `tsc`/editor consumers saw it). Runtime was always fine — `defineExtension` validates `values ?? {}`. - Don't drop the cast when editing the schema, and don't try to fix it with `.optional()`/`.default({})` - on the schema: that only widens the *type* of the argument, not its optionality. - Guarded by `test/mount-config.test.ts` (a zero-arg mount + a `@ts-expect-error` bad field) — the - package's `typecheck` covers `test/`, so removing the cast fails `pnpm typecheck`, not just review. Config knobs: `userId` (string or `(ctx: SessionContext) => string` — eve's public base of tool+hook ctx, imported from `eve/tools`), `redis` (defaults `Redis.fromEnv()`), `memory{topK,minScore}`, `search{schema,indexName,prefix,defaultLimit}`, `chatHistory: boolean | {prefix,indexName,ttlSeconds}` diff --git a/packages/eve-extension/AGENTS.md b/packages/eve-extension/AGENTS.md index d69262f..c4a5e07 100644 --- a/packages/eve-extension/AGENTS.md +++ b/packages/eve-extension/AGENTS.md @@ -15,10 +15,6 @@ unavailable, use https://eve.dev/docs/extensions as a fallback. - Declare the extension in `extension/extension.ts` with `defineExtension` from `eve/extension`. Config is optional; read bound values via the handle's `.config` in tools and hooks. - Because every config field here is optional, the default export is cast to a - type whose config **parameter** is optional too — eve's `ExtensionHandle` call - signature requires the argument, which made the documented `agentkit()` mount - fail `tsc` with TS2554. Keep that cast if you touch the schema. - Add contributions under `extension/` the same way as in an agent: `tools/`, `channels/`, `connections/`, `skills/`, `schedules/`, `subagents/`, `hooks/`, and optional instruction fragments (eve ≥0.41 supports the full set; diff --git a/packages/eve-extension/extension/extension.ts b/packages/eve-extension/extension/extension.ts index 37f2e6f..db06459 100644 --- a/packages/eve-extension/extension/extension.ts +++ b/packages/eve-extension/extension/extension.ts @@ -1,4 +1,4 @@ -import { defineExtension, type ExtensionHandle, type MountedExtension } from "eve/extension"; +import { defineExtension } from "eve/extension"; import type { SessionContext } from "eve/tools"; import { z } from "zod"; import type { Redis } from "@upstash/redis"; @@ -16,76 +16,58 @@ const userId = z.union([ z.custom<(ctx: SessionContext) => string>((value) => typeof value === "function"), ]); -const configSchema = z.object({ - userId: userId.optional(), - /** Upstash Redis client. Defaults to `Redis.fromEnv()` (`UPSTASH_REDIS_REST_URL`/`_TOKEN`). */ - redis: z.custom((value) => typeof value === "object" && value !== null).optional(), - /** - * Report the sdk name + version to Upstash as a header on the requests made by the redis client. - * Can also be disabled with the `UPSTASH_DISABLE_TELEMETRY` env var. Defaults to `true`. - */ - enableTelemetry: z.boolean().optional(), - /** Knobs for the `recall_memory` tool. */ - memory: z - .object({ - /** Max memories returned by a recall. */ - topK: z.number().int().positive().optional(), - /** Minimum BM25 relevance score for recall hits. */ - minScore: z.number().optional(), - }) - .optional(), - /** - * Enables the `search` / `aggregate` / `count` tools over one Upstash Redis Search index. Without - * this, those tools error at call time — configure it, or disable their slots with `disableTool()`. - */ - search: z - .object({ - /** The index schema, built with `s` from `@upstash/redis`. */ - schema: z.custom((value) => typeof value === "object" && value !== null), - /** Index name. Defaults to `"agentkit:search"`. */ - indexName: z.string().min(1).optional(), - /** Key prefix for indexed JSON documents. Defaults to `":"`. */ - prefix: z.string().min(1).optional(), - /** Default page size for the `search` tool. Defaults to 10. */ - defaultLimit: z.number().int().positive().optional(), - }) - .optional(), - /** - * Durable transcript capture into Upstash Redis `ChatHistory` (**off by default**): a hook - * appends every user and assistant message as it streams, keyed by `userId` + session id. Pass - * `true` to enable it with defaults, or an object to enable it and tune where chats are stored. - */ - chatHistory: z - .union([ - z.boolean(), - z.object({ - /** Base key prefix for stored chats; defaults to `agentkit:chat`. */ - prefix: z.string().min(1).optional(), - /** Redis Search index name. Defaults to the (identifier-safe) `prefix`. */ +export default defineExtension({ + config: z.object({ + userId: userId.optional(), + /** Upstash Redis client. Defaults to `Redis.fromEnv()` (`UPSTASH_REDIS_REST_URL`/`_TOKEN`). */ + redis: z.custom((value) => typeof value === "object" && value !== null).optional(), + /** + * Report the sdk name + version to Upstash as a header on the requests made by the redis client. + * Can also be disabled with the `UPSTASH_DISABLE_TELEMETRY` env var. Defaults to `true`. + */ + enableTelemetry: z.boolean().optional(), + /** Knobs for the `recall_memory` tool. */ + memory: z + .object({ + /** Max memories returned by a recall. */ + topK: z.number().int().positive().optional(), + /** Minimum BM25 relevance score for recall hits. */ + minScore: z.number().optional(), + }) + .optional(), + /** + * Enables the `search` / `aggregate` / `count` tools over one Upstash Redis Search index. Without + * this, those tools error at call time — configure it, or disable their slots with `disableTool()`. + */ + search: z + .object({ + /** The index schema, built with `s` from `@upstash/redis`. */ + schema: z.custom((value) => typeof value === "object" && value !== null), + /** Index name. Defaults to `"agentkit:search"`. */ indexName: z.string().min(1).optional(), - /** Optional TTL (seconds) per chat. Omit for no expiry. */ - ttlSeconds: z.number().int().positive().optional(), - }), - ]) - .optional(), + /** Key prefix for indexed JSON documents. Defaults to `":"`. */ + prefix: z.string().min(1).optional(), + /** Default page size for the `search` tool. Defaults to 10. */ + defaultLimit: z.number().int().positive().optional(), + }) + .optional(), + /** + * Durable transcript capture into Upstash Redis `ChatHistory` (**off by default**): a hook + * appends every user and assistant message as it streams, keyed by `userId` + session id. Pass + * `true` to enable it with defaults, or an object to enable it and tune where chats are stored. + */ + chatHistory: z + .union([ + z.boolean(), + z.object({ + /** Base key prefix for stored chats; defaults to `agentkit:chat`. */ + prefix: z.string().min(1).optional(), + /** Redis Search index name. Defaults to the (identifier-safe) `prefix`. */ + indexName: z.string().min(1).optional(), + /** Optional TTL (seconds) per chat. Omit for no expiry. */ + ttlSeconds: z.number().int().positive().optional(), + }), + ]) + .optional(), + }), }); - -/** The mount config. Every field is optional, so the whole object may be omitted entirely. */ -type AgentkitConfig = z.input; - -/** - * The mount factory this package default-exports, with the config argument made **optional**. - * - * eve types `ExtensionHandle`'s call signature with a required `values` argument even when every - * field of the config schema is optional, so a bare `agentkit()` fails `tsc` with TS2554 (`eve build` - * doesn't typecheck, so it only bites consumers in an editor / `tsc`). eve's runtime already accepts - * the omitted argument — `defineExtension` validates `values ?? {}` — so this only widens the type to - * match the documented API: the smallest mount is `agentkit()`. - */ -type HandleMembers = Pick, "config" | "schema">; - -interface AgentkitExtension extends HandleMembers { - (config?: AgentkitConfig): MountedExtension; -} - -export default defineExtension({ config: configSchema }) as AgentkitExtension; diff --git a/packages/eve-extension/test/mount-config.test.ts b/packages/eve-extension/test/mount-config.test.ts deleted file mode 100644 index 8c8cb16..0000000 --- a/packages/eve-extension/test/mount-config.test.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { describe, expect, test } from "vitest"; -import agentkit from "../extension/extension"; - -/** - * Every config field is optional, so the README's smallest mount is a bare `agentkit()`. eve types - * `ExtensionHandle`'s call signature with a *required* argument regardless, which made that mount - * fail `tsc` with TS2554, so `extension.ts` re-types the default export with an optional parameter. - * This file guards both halves of that: the zero-argument call has to compile (the package's - * `typecheck` script covers `test/`) and still produce a mounted extension, and a config that *is* - * passed has to keep being validated field by field. - */ -const MOUNTED_EXTENSION = Symbol.for("eve.mounted-extension"); - -describe("mount factory", () => { - test("mounts with no config at all", () => { - const mounted = agentkit(); - - expect(Object.getOwnPropertySymbols(mounted)).toContain(MOUNTED_EXTENSION); - expect(agentkit.config).toEqual({}); - }); - - test("mounts with an empty config object", () => { - expect(Object.getOwnPropertySymbols(agentkit({}))).toContain(MOUNTED_EXTENSION); - }); - - test("still validates the fields it is given", () => { - // @ts-expect-error — an optional parameter must not weaken per-field type checking - expect(() => agentkit({ memory: { topK: "nope" } })).toThrow(/Invalid extension config/); - }); -}); From 3fe8a88225fc5ea65dea93260bd55df2d0749b2b Mon Sep 17 00:00:00 2001 From: "upstash-tag[bot]" <313023939+upstash-tag[bot]@users.noreply.github.com> Date: Tue, 8 Sep 2026 12:09:34 +0000 Subject: [PATCH 3/4] docs(eve-extension): use agentkit({}) in the README, with a changeset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit eve types the mount handle it returns as (values: InferInput) — a required parameter regardless of how optional the config schema is — so the README example export default agentkit(); failed consumers' tsc with TS2554 even though it works at runtime (defineExtension validates values ?? {}). That signature lives in the eve peer dependency, not in this package, so the example is what changes: it now mounts with an empty config, which typechecks against the published types. Adds a short note in the package AGENTS.md so the example is not "corrected" back, and a patch changeset so the fixed README reaches npm. Co-Authored-By: Claude Opus 5 --- ...eve-extension-readme-empty-config-mount.md | 35 +++++++++++++++++++ packages/eve-extension/AGENTS.md | 5 +++ packages/eve-extension/README.md | 4 +-- 3 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 .changeset/eve-extension-readme-empty-config-mount.md diff --git a/.changeset/eve-extension-readme-empty-config-mount.md b/.changeset/eve-extension-readme-empty-config-mount.md new file mode 100644 index 0000000..c95baeb --- /dev/null +++ b/.changeset/eve-extension-readme-empty-config-mount.md @@ -0,0 +1,35 @@ +--- +"@upstash/agentkit-eve-extension": patch +--- + +docs: correct the README's minimal mount to `agentkit({})` + +The "Mount it" example showed `export default agentkit();`, which **fails a consumer's +`tsc`** with `TS2554: Expected 1 arguments, but got 0.` — verified against the published +`0.7.0` types, and against a build of the current source. It only ever bit consumers in an +editor or running `tsc`, because `eve build` does not typecheck the mount file, and it works +at runtime: eve's `defineExtension` validates `values ?? {}`, so a zero-argument mount binds +an empty config just fine. The example is now `export default agentkit({});`, which +typechecks clean. + +The required argument is **not** something this package declares. Every field of the config +schema is optional, but eve types the mount handle it returns as + +```ts +export interface ExtensionHandle { + (values: StandardSchemaV1.InferInput): MountedExtension; + // … +} +``` + +— a *required* parameter, and TypeScript will not let a required parameter be omitted even +when its type admits `undefined`. That interface lives in the `eve` peer dependency, which +supplies the type at the consumer's own install, so the zero-argument form cannot be made to +typecheck from this repo: marking `defineExtension`'s `config` property optional does not +reach the handle's call signature (still `TS2554`), and `.optional()` on the config schema +does not either — it only makes `extension.config` possibly-undefined and breaks the +extension's own build. The upstream fix would be `(values?: …)` on `ExtensionHandle`; if eve +ships that, the example can go back to `agentkit()`. + +Docs only — no extension source, contributions or manifest changed, so the built `dist/` and +the `eve` peer floor are untouched. This ships purely so the corrected README reaches npm. diff --git a/packages/eve-extension/AGENTS.md b/packages/eve-extension/AGENTS.md index c4a5e07..3e2c3dd 100644 --- a/packages/eve-extension/AGENTS.md +++ b/packages/eve-extension/AGENTS.md @@ -15,6 +15,11 @@ unavailable, use https://eve.dev/docs/extensions as a fallback. - Declare the extension in `extension/extension.ts` with `defineExtension` from `eve/extension`. Config is optional; read bound values via the handle's `.config` in tools and hooks. + Note: even when every config *field* is optional, eve types the mount handle's + call signature as `(values: InferInput)` — a **required** parameter — so + `agentkit()` fails `tsc` with TS2554 and docs must show `agentkit({})`. That + signature lives in the `eve` peer dep, not here; `.optional()` on the schema + does not fix it (it only makes `extension.config` possibly-undefined). - Add contributions under `extension/` the same way as in an agent: `tools/`, `channels/`, `connections/`, `skills/`, `schedules/`, `subagents/`, `hooks/`, and optional instruction fragments (eve ≥0.41 supports the full set; diff --git a/packages/eve-extension/README.md b/packages/eve-extension/README.md index 24b3a8a..66d5aaa 100644 --- a/packages/eve-extension/README.md +++ b/packages/eve-extension/README.md @@ -26,13 +26,13 @@ Set `UPSTASH_REDIS_REST_URL` / `UPSTASH_REDIS_REST_TOKEN` in your env (the exten ## Mount it -Every field is optional. The smallest mount gives the agent memory tools: +Every field is optional, so the smallest mount is an empty config — it gives the agent memory tools: ```ts // agent/extensions/agentkit.ts import agentkit from "@upstash/agentkit-eve-extension"; -export default agentkit(); +export default agentkit({}); ``` Add `search` to turn on the search tools over one index. The schema is built with `s` from From aa363692af0eb60ea71efa68a4bf2c7503bd5028 Mon Sep 17 00:00:00 2001 From: "upstash-tag[bot]" <313023939+upstash-tag[bot]@users.noreply.github.com> Date: Tue, 8 Sep 2026 14:04:36 +0000 Subject: [PATCH 4/4] chore: drop changeset for README-only fix --- ...eve-extension-readme-empty-config-mount.md | 35 ------------------- 1 file changed, 35 deletions(-) delete mode 100644 .changeset/eve-extension-readme-empty-config-mount.md diff --git a/.changeset/eve-extension-readme-empty-config-mount.md b/.changeset/eve-extension-readme-empty-config-mount.md deleted file mode 100644 index c95baeb..0000000 --- a/.changeset/eve-extension-readme-empty-config-mount.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -"@upstash/agentkit-eve-extension": patch ---- - -docs: correct the README's minimal mount to `agentkit({})` - -The "Mount it" example showed `export default agentkit();`, which **fails a consumer's -`tsc`** with `TS2554: Expected 1 arguments, but got 0.` — verified against the published -`0.7.0` types, and against a build of the current source. It only ever bit consumers in an -editor or running `tsc`, because `eve build` does not typecheck the mount file, and it works -at runtime: eve's `defineExtension` validates `values ?? {}`, so a zero-argument mount binds -an empty config just fine. The example is now `export default agentkit({});`, which -typechecks clean. - -The required argument is **not** something this package declares. Every field of the config -schema is optional, but eve types the mount handle it returns as - -```ts -export interface ExtensionHandle { - (values: StandardSchemaV1.InferInput): MountedExtension; - // … -} -``` - -— a *required* parameter, and TypeScript will not let a required parameter be omitted even -when its type admits `undefined`. That interface lives in the `eve` peer dependency, which -supplies the type at the consumer's own install, so the zero-argument form cannot be made to -typecheck from this repo: marking `defineExtension`'s `config` property optional does not -reach the handle's call signature (still `TS2554`), and `.optional()` on the config schema -does not either — it only makes `extension.config` possibly-undefined and breaks the -extension's own build. The upstream fix would be `(values?: …)` on `ExtensionHandle`; if eve -ships that, the example can go back to `agentkit()`. - -Docs only — no extension source, contributions or manifest changed, so the built `dist/` and -the `eve` peer floor are untouched. This ships purely so the corrected README reaches npm.