Repository navigation
perf(sdk): precompile tool-schema TypeScript during indexing - #49
Conversation
executor.tools.schema always renders the TypeScript preview now. The reindex pipeline -- the only caller that passed includeTypeScript:false to skip codegen -- calls it plainly, so each reindex warms the exact KV schema-view the web UI reads. First tool-detail views become cache hits instead of a cold compile. Embedding text is unchanged (collectDocForTool only reads raw schema facets, never the rendered TypeScript). Removes the includeTypeScript option + the now-empty ToolSchemaOptions type, and drops the flag from the schema-view cache key (old entries re-warm on next index/view).
Greptile SummaryThis PR eliminates the two-tier
Confidence Score: 4/5The cache-warming logic and API simplification are correct; the main concern is the changeset classification of a public API removal. The
Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant RI as Reindex Pipeline
participant SDK as executor.tools.schema
participant KV as KV Cache
participant TS as TypeScript Codegen
participant UI as Web UI
note over RI,UI: Before this PR
RI->>SDK: "schema(addr, { includeTypeScript: false })"
SDK->>KV: get(key[false])
KV-->>SDK: miss
SDK-->>RI: raw schema (no TypeScript)
UI->>SDK: schema(addr)
SDK->>KV: get(key[true])
KV-->>SDK: miss (different key variant)
SDK->>TS: buildCachedToolTypeScriptPreview(...)
TS-->>SDK: preview
SDK->>KV: set(key[true], view)
SDK-->>UI: view with TypeScript (cold compile on first view)
note over RI,UI: After this PR
RI->>SDK: schema(addr)
SDK->>KV: get(unified_key)
KV-->>SDK: miss (first reindex)
SDK->>TS: buildCachedToolTypeScriptPreview(...)
TS-->>SDK: preview
SDK->>KV: set(unified_key, view)
SDK-->>RI: view (TypeScript included, cache warmed)
UI->>SDK: schema(addr)
SDK->>KV: get(unified_key)
KV-->>SDK: hit
SDK-->>UI: view with TypeScript (cache hit)
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant RI as Reindex Pipeline
participant SDK as executor.tools.schema
participant KV as KV Cache
participant TS as TypeScript Codegen
participant UI as Web UI
note over RI,UI: Before this PR
RI->>SDK: "schema(addr, { includeTypeScript: false })"
SDK->>KV: get(key[false])
KV-->>SDK: miss
SDK-->>RI: raw schema (no TypeScript)
UI->>SDK: schema(addr)
SDK->>KV: get(key[true])
KV-->>SDK: miss (different key variant)
SDK->>TS: buildCachedToolTypeScriptPreview(...)
TS-->>SDK: preview
SDK->>KV: set(key[true], view)
SDK-->>UI: view with TypeScript (cold compile on first view)
note over RI,UI: After this PR
RI->>SDK: schema(addr)
SDK->>KV: get(unified_key)
KV-->>SDK: miss (first reindex)
SDK->>TS: buildCachedToolTypeScriptPreview(...)
TS-->>SDK: preview
SDK->>KV: set(unified_key, view)
SDK-->>RI: view (TypeScript included, cache warmed)
UI->>SDK: schema(addr)
SDK->>KV: get(unified_key)
KV-->>SDK: hit
SDK-->>UI: view with TypeScript (cache hit)
|
**Phase 1** of Tier-2 tool-catalog caching — stacked on #49. Caches `executor.tools.list` (today a full `tool` scan + `tool_policy` read on every call) behind a **content-derived revision key**, so the list stays warm and only re-reads D1 when the catalog or policies actually change. No TTL. ## Invalidation - `produceConnectionTools` writes a per-connection **`toolset_revision`** = `sha256` of the connection's manifest rows' `(path, index_fingerprint)`, in **`plugin_storage`** (owner-scoped, **no schema migration**), at all 5 catalog-mutation sites (produce, both clear branches, connection-remove, integration-remove). - `catalogGen` combines the in-scope connections' revisions; `policyGen` combines the `tool_policy` rows. Key = `sha256(owner, integration, connection, includeBlocked, catalogGen, policyGen)`. - **Token rotation does not bust** (never calls `produceConnectionTools`); add/remove/change a tool, or any policy edit, does. ## Scope - Cached value = the DB-derived, policy-filtered list. **Static tools unioned live** (never cached); **`query` applied post-cache** so distinct queries share one cached set. - Hand-authored `ToolListItem` Schema (list projection only — no input/output schema); branded ids stored as strings, rebuilt on read. ## Notes (intentional, single-tenant self-host) - `catalogGen`/`policyGen` reads are tenant-wide → per-tenant-coarse invalidation (a catalog change re-warms all owners' list caches). Correct, just coarse. ## Gate tsgo (sdk) · `oxlint --deny-warnings` · oxfmt · vitest **sdk 350/350** (new cache suite: add/remove/content invalidation, policy bust, query post-cache, static-union-on-hit). Phase 2 (stacked) moves the UI list onto the compact manifest tier. <!-- stack:links:start --> ### [Stack](https://github.com/kitlangton/stack) 1. #49 2. **#50** 👈 current <!-- stack:links:end -->
## Why
Production traces showed the web UI's tool-detail path (`GET
/api/tools/schema`, `includeTypeScript: true`) paying the json-schema →
TypeScript compile on the first view of every tool. The reindex pipeline
already visits every tool's schema, but it called `tools.schema({
includeTypeScript: false })` specifically to *skip* that compile — so it
warmed the wrong cache variant and the UI's variant stayed cold until
first manual view.
## What
- `executor.tools.schema` always renders the TypeScript preview; the
`includeTypeScript` option and the now-empty `ToolSchemaOptions` type
are removed.
- The reindex pipeline (`collectDocForTool`) calls `tools.schema`
plainly, so every reindex pre-warms exactly the KV schema-view the UI
reads → first tool-detail views become cache hits instead of a cold
compile.
- `includeTypeScript` is dropped from the schema-view cache key (content
+ manifest), collapsing the two variants into one.
## Safety
- Embedding/lexical text is unchanged: `collectDocForTool` only reads
the raw schema facets (`inputSchema` / `outputSchema` /
`schemaDefinitions`), never the rendered TypeScript — so no re-embedding
/ index churn.
- The cache-key change supersedes existing entries; they re-warm on the
next reindex or view (regenerable derived data).
## Gate
tsgo (43/43), `oxlint --deny-warnings`, oxfmt, and vitest for
`@executor-js/sdk` + `@executor-js/plugin-semantic-search` all green.
Follow-up: a reindex on the deployed instance warms the new unified
variant.
<!-- stack:links:start -->
### [Stack](https://github.com/kitlangton/stack)
1. **#49** 👈 current
2. #50
<!-- stack:links:end -->
**Phase 1** of Tier-2 tool-catalog caching — stacked on #49. Caches `executor.tools.list` (today a full `tool` scan + `tool_policy` read on every call) behind a **content-derived revision key**, so the list stays warm and only re-reads D1 when the catalog or policies actually change. No TTL. - `produceConnectionTools` writes a per-connection **`toolset_revision`** = `sha256` of the connection's manifest rows' `(path, index_fingerprint)`, in **`plugin_storage`** (owner-scoped, **no schema migration**), at all 5 catalog-mutation sites (produce, both clear branches, connection-remove, integration-remove). - `catalogGen` combines the in-scope connections' revisions; `policyGen` combines the `tool_policy` rows. Key = `sha256(owner, integration, connection, includeBlocked, catalogGen, policyGen)`. - **Token rotation does not bust** (never calls `produceConnectionTools`); add/remove/change a tool, or any policy edit, does. - Cached value = the DB-derived, policy-filtered list. **Static tools unioned live** (never cached); **`query` applied post-cache** so distinct queries share one cached set. - Hand-authored `ToolListItem` Schema (list projection only — no input/output schema); branded ids stored as strings, rebuilt on read. - `catalogGen`/`policyGen` reads are tenant-wide → per-tenant-coarse invalidation (a catalog change re-warms all owners' list caches). Correct, just coarse. tsgo (sdk) · `oxlint --deny-warnings` · oxfmt · vitest **sdk 350/350** (new cache suite: add/remove/content invalidation, policy bust, query post-cache, static-union-on-hit). Phase 2 (stacked) moves the UI list onto the compact manifest tier. <!-- stack:links:start --> 1. #49 2. **#50** 👈 current <!-- stack:links:end -->
## Why
Production traces showed the web UI's tool-detail path (`GET
/api/tools/schema`, `includeTypeScript: true`) paying the json-schema →
TypeScript compile on the first view of every tool. The reindex pipeline
already visits every tool's schema, but it called `tools.schema({
includeTypeScript: false })` specifically to *skip* that compile — so it
warmed the wrong cache variant and the UI's variant stayed cold until
first manual view.
## What
- `executor.tools.schema` always renders the TypeScript preview; the
`includeTypeScript` option and the now-empty `ToolSchemaOptions` type
are removed.
- The reindex pipeline (`collectDocForTool`) calls `tools.schema`
plainly, so every reindex pre-warms exactly the KV schema-view the UI
reads → first tool-detail views become cache hits instead of a cold
compile.
- `includeTypeScript` is dropped from the schema-view cache key (content
+ manifest), collapsing the two variants into one.
## Safety
- Embedding/lexical text is unchanged: `collectDocForTool` only reads
the raw schema facets (`inputSchema` / `outputSchema` /
`schemaDefinitions`), never the rendered TypeScript — so no re-embedding
/ index churn.
- The cache-key change supersedes existing entries; they re-warm on the
next reindex or view (regenerable derived data).
## Gate
tsgo (43/43), `oxlint --deny-warnings`, oxfmt, and vitest for
`@executor-js/sdk` + `@executor-js/plugin-semantic-search` all green.
Follow-up: a reindex on the deployed instance warms the new unified
variant.
<!-- stack:links:start -->
### [Stack](https://github.com/kitlangton/stack)
1. **#49** 👈 current
2. #50
<!-- stack:links:end -->
**Phase 1** of Tier-2 tool-catalog caching — stacked on #49. Caches `executor.tools.list` (today a full `tool` scan + `tool_policy` read on every call) behind a **content-derived revision key**, so the list stays warm and only re-reads D1 when the catalog or policies actually change. No TTL. - `produceConnectionTools` writes a per-connection **`toolset_revision`** = `sha256` of the connection's manifest rows' `(path, index_fingerprint)`, in **`plugin_storage`** (owner-scoped, **no schema migration**), at all 5 catalog-mutation sites (produce, both clear branches, connection-remove, integration-remove). - `catalogGen` combines the in-scope connections' revisions; `policyGen` combines the `tool_policy` rows. Key = `sha256(owner, integration, connection, includeBlocked, catalogGen, policyGen)`. - **Token rotation does not bust** (never calls `produceConnectionTools`); add/remove/change a tool, or any policy edit, does. - Cached value = the DB-derived, policy-filtered list. **Static tools unioned live** (never cached); **`query` applied post-cache** so distinct queries share one cached set. - Hand-authored `ToolListItem` Schema (list projection only — no input/output schema); branded ids stored as strings, rebuilt on read. - `catalogGen`/`policyGen` reads are tenant-wide → per-tenant-coarse invalidation (a catalog change re-warms all owners' list caches). Correct, just coarse. tsgo (sdk) · `oxlint --deny-warnings` · oxfmt · vitest **sdk 350/350** (new cache suite: add/remove/content invalidation, policy bust, query post-cache, static-union-on-hit). Phase 2 (stacked) moves the UI list onto the compact manifest tier. <!-- stack:links:start --> 1. #49 2. **#50** 👈 current <!-- stack:links:end -->
Production traces showed the web UI's tool-detail path (`GET
/api/tools/schema`, `includeTypeScript: true`) paying the json-schema →
TypeScript compile on the first view of every tool. The reindex pipeline
already visits every tool's schema, but it called `tools.schema({
includeTypeScript: false })` specifically to *skip* that compile — so it
warmed the wrong cache variant and the UI's variant stayed cold until
first manual view.
- `executor.tools.schema` always renders the TypeScript preview; the
`includeTypeScript` option and the now-empty `ToolSchemaOptions` type
are removed.
- The reindex pipeline (`collectDocForTool`) calls `tools.schema`
plainly, so every reindex pre-warms exactly the KV schema-view the UI
reads → first tool-detail views become cache hits instead of a cold
compile.
- `includeTypeScript` is dropped from the schema-view cache key (content
+ manifest), collapsing the two variants into one.
- Embedding/lexical text is unchanged: `collectDocForTool` only reads
the raw schema facets (`inputSchema` / `outputSchema` /
`schemaDefinitions`), never the rendered TypeScript — so no re-embedding
/ index churn.
- The cache-key change supersedes existing entries; they re-warm on the
next reindex or view (regenerable derived data).
tsgo (43/43), `oxlint --deny-warnings`, oxfmt, and vitest for
`@executor-js/sdk` + `@executor-js/plugin-semantic-search` all green.
Follow-up: a reindex on the deployed instance warms the new unified
variant.
<!-- stack:links:start -->
1. **#49** 👈 current
2. #50
<!-- stack:links:end -->
Why
Production traces showed the web UI's tool-detail path (
GET /api/tools/schema,includeTypeScript: true) paying the json-schema → TypeScript compile on the first view of every tool. The reindex pipeline already visits every tool's schema, but it calledtools.schema({ includeTypeScript: false })specifically to skip that compile — so it warmed the wrong cache variant and the UI's variant stayed cold until first manual view.What
executor.tools.schemaalways renders the TypeScript preview; theincludeTypeScriptoption and the now-emptyToolSchemaOptionstype are removed.collectDocForTool) callstools.schemaplainly, so every reindex pre-warms exactly the KV schema-view the UI reads → first tool-detail views become cache hits instead of a cold compile.includeTypeScriptis dropped from the schema-view cache key (content + manifest), collapsing the two variants into one.Safety
collectDocForToolonly reads the raw schema facets (inputSchema/outputSchema/schemaDefinitions), never the rendered TypeScript — so no re-embedding / index churn.Gate
tsgo (43/43),
oxlint --deny-warnings, oxfmt, and vitest for@executor-js/sdk+@executor-js/plugin-semantic-searchall green.Follow-up: a reindex on the deployed instance warms the new unified variant.
Stack