Kody v2026.07.30
This release reshapes how packages work and how fast they run. The package model is now static-first — two simple rules replace a tangle of invocation paths — and hosted package apps moved to their own origin so community code can never touch your session. On the performance side, execute latency got a hard look: cold-isolate dispatch dropped ~1.5 seconds, sequential storage round trips came off the hot path, and every execute now reports phase-level timings so you can see exactly where time goes.
New
-
A simpler, faster package model — two rules, not five. When you know a package's name at write time,
import fn from 'kody:@scope/pkg/export'is now the default: typed, publish-verified, and essentially free per call. When the name is data or you need the target's own runtime,packages.invoke({ kodyId, exportName, params })is the one dynamic primitive — keyless for lean/ephemeral calls, keyed with anidempotencyKeyfor durable exactly-once dispatch. Legacy surfaces (packages.invokeChecked,packages.check, dynamicimport("kody:@…")) still work but are deprecated everywhere Kody's agent guidance talks to you. All agent-facing instructions, MCP tool descriptions, and docs now teach the two-rule model consistently. (#1045, #1046) -
Static import calls are now metered. Every call through a statically imported package export emits a non-blocking usage event with the caller, callee package, duration, and outcome — so package reuse shows up on your admin usage charts the same way dynamic invocations always have. (#1047)
-
Hosted package apps now run on a separate origin (
kodyapps.dev). Community package code executed on the app origin could read your session cookie or fetch your account data withcredentials: 'include'. Package apps now live on their own registrable domain: credentials are stripped from every forwarded request and WebSocket upgrade, and same-origin browser attacks are structurally impossible. Package search, identity parsing, and hosted URLs all follow the new origin. A hotfix and a follow-up fixed a production outage where deploying the new domain accidentally detachedheykody.devand disabled theworkers.devtrigger — both are now guarded so a deploy can never drop a domain again. (#1013, #1015, #1016, #1017, #1025) -
Optional pre-exec TypeScript checks for
execute. A feature-flagged LanguageService check type-checks your ad hoc execute modules — includingkody:@static imports — before bundling and sandbox execution, catching type errors before the run starts. Default off, with a per-user override available. (#1029) -
Execute now reports phase-level Server-Timing. Every execute response carries
serverTimingentries for typecheck, bundle, and run phases — and the run phase is further split into hydrate, provider-assembly, and sandbox sub-spans. When a run feels slow, you can see exactly which phase owns the latency directly from the tool response. (#1034, #1036) -
Package-storage audit route for admins.
GET /admin/package-storage-audit.jsongives a read-only, platform-wide view of legacy storage buckets, ambientstorageimports, and orphaned inventory rows — the report that gates the ongoing cleanup of old package storage layouts. (#1030)
Improved
-
Cold-isolate dispatch is ~1.5 seconds faster. The first
kody.*capability call inside a fresh isolate was paying for lazy module evaluation of ~500 modules; the hot path now skips that and lands in the same ~40ms band as warm calls. Sequential storage round trips were also cut from the execute hot path — package invocations that stacked 600ms–1.9s of platform overhead now resolve in tens of milliseconds on the lean path. (#1042, #1035, #1046) -
Scheduled jobs are safer and self-healing. Due jobs are now atomically claimed with expiring leases so two workers can't double-run the same occurrence, transient failures retry with exponential backoff, and each occurrence is deduplicated by a
{jobId, scheduledFor}key. Kody also now detects silently dead scheduled jobs — schedules whose alarms stopped firing without any error — and re-arms them automatically. (#1012, #1041) -
Package delete now cleans up everything it owns. Deleting a package previously orphaned durable storage buckets, app-scoped values, and job scratch state. It now clears every package-owned bucket with per-bucket error tolerance and removes app-scoped values alongside secrets — and app-scoped values are keyed by package id only, so non-package contexts can no longer mint storage buckets under arbitrary keys. Package publish artifact rebuilds also moved to throwaway isolates so large packages can't hit memory limits during rebuild. (#1022, #1026, #1028)
-
Storage entitlement checks are accurate across all buckets. Quota enforcement now aggregates every inventoried storage bucket — including the current bucket before async registration finishes — instead of silently under-counting. Estimates fail closed if any bucket is unreadable, and a bounded retry protects against transient flakes during package deletions. (#1010, #1030)
-
Homepage is ~75% lighter and scores 100 across every PageSpeed category. Route-level code splitting, lazy Sentry, and image optimization cut the critical-path weight from 937KB to a fraction; the hero PNG alone was 339KB. Every remaining PageSpeed audit is now green — doctype fixed (no more quirks mode), CSS inlined for first paint, meta descriptions added, CSP allowlisted for Cloudflare's analytics beacon, and color contrast raised above the AA threshold. (#1021, #1027)
-
Import boundaries are now enforced by lint. The MCP layer can no longer reach into app-layer modules, and the package registry can no longer import MCP internals — a custom lint rule catches static, dynamic, and test-mock imports alike. Six compatibility re-export shims left from the extraction were removed so the coupling can't quietly return. (#1014, #1018)
-
Client route state is deduplicated. Five hand-rolled delete-confirmation flows now share one
createDoubleCheckhelper, and Account Secrets moved to the shared list/detail route helper with nested-path support — no URL changes. (#1011) -
OAuth connect helpers extracted and tested. The 2,000-line connect-oauth route was split into a pure-helper module with full characterization tests, and client/server secret normalization now has parity tests so the two sides can't silently drift. (#1019)
-
Docs audited against reality. Every markdown file was checked against current code — wrong ports, stale migration references, contradictory setup instructions, and duplicated architecture docs were corrected. The disaster-recovery runbook now records which Cloudflare resources are intentionally kept on the old account so a future cleanup can't delete live backups. (#1023, #1020, #1043)
Fixed
-
Execute hangs past 90 seconds are gone, and stranded runs self-heal. The sandbox timeout only applied inside the dynamic worker, so
evaluate()could hang indefinitely on the host side — leaving runs stuck for hours with no terminal record. There's now a host-side deadline racing the worker evaluation, timed-out queued evaluations are aborted so they can't start late, and outbound fetches have a default timeout. Strandedrunningrows are marked terminal instead of waiting 24 hours. (#1039) -
Package service caller mistakes no longer open Sentry issues. When an agent called a package service without a
package_idor with an unknown package, the error looked like a platform bug. Those are now classified as caller errors and filtered from Sentry. (#1038)