Skip to content

walkerOS v4.0.0

Choose a tag to compare

@github-actions github-actions released this 04 May 14:32
· 195 commits to main since this release
3764cc0

Changes

Remove dead bundleRemote() and add OpenAPI drift detection.
Breaking changes:

  • Removed bundleRemote() export from @walkeros/cli. The corresponding
    /api/bundle endpoint was removed from the walkerOS app on 2026-04-08, so
    this function had been silently broken in production for ~3 weeks. Local
    bundling via bundle() is unaffected.
  • Removed remote and content options from the MCP flow_bundle tool. The
    tool now bundles locally only.
    New:
  • Added npm run -w @walkeros/cli validate:openapi-spec script that diffs the
    checked-in packages/cli/openapi/spec.json against the live app's OpenAPI
    document. Detects drift between the walkerOS-side type contract and the actual
    API. Wired into PR-time CI, daily cron, and a pre-commit lint-staged hook. All
    layers are gated on a WALKEROS_APP_URL secret and skip silently when unset,
    so the change ships safely without configuration. To activate: set
    WALKEROS_APP_URL in repo secrets pointing to a deployed app instance.

Fix walkeros push deadlock for web flows whose destinations await real timers
during init.
Previously, async-drain timer interception captured every setTimeout into a
pending map and only fired them via a post-fn flush. If a destination's init
awaited one of those captured timers (e.g.,
@walkeros/web-destination-amplitude's engagement plugin awaits a 10s
setTimeout to give up on a CDN script load), init never resolved,
await collector.push deadlocked, and Node exited with
Detected unsettled top-level await (exit 13).
A drain pump now runs alongside fn(flowModule) for non---simulate runs: each
tick fires every captured non-cleared timer using a real setImmediate
reference. Timers fire in delay-ascending order, intervals re-register, callback
errors are reported via console.warn. Bounded by max-iterations (1000) and
wall-clock (30s) caps.
--simulate <step> continues to use the post-fn flush path so snapshot
ordering remains stable.
Behavior change (edge case): a destination using setTimeout for retry backoff
under walkeros push (real, non-simulate) now sees its timer fire instantly.
This was already the documented contract for --simulate <step> snapshots; it
now extends to real push for consistency.

Validate device-code and token responses from the auth server with Zod schemas
at the trust boundary in login/index.ts. Malformed responses now surface as
structured errors instead of being trusted into config writes or browser
launches. Replaces the hand-rolled type guards for Items 1 and 3 of the cli-auth
feedback review. No public API change.

Explicit opt-in anonymous usage telemetry for CLI and MCP. Telemetry is off by
default; users opt in with walkeros telemetry enable and out with
walkeros telemetry disable. No persistent identifier is written before opt-in.
No ingest endpoint ships in this release: opting in records consent locally;
emission begins when a managed endpoint is released. The data contract lives at
packages/cli/src/telemetry/flow.json.

Add flowId filter to CLI listDeployments and redesign the MCP
deploy_manage tool around it.
CLI (@walkeros/cli):

  • listDeployments({ projectId?, type?, status?, flowId? }) now forwards
    flowId as a query parameter to GET /api/projects/{id}/deployments.
  • New helper deleteDeploymentByFlowId({ projectId?, flowId, slug? }) deletes
    the active deployment for a flow, surfacing a DeploymentAmbiguityError (code
    MULTIPLE_DEPLOYMENTS, with a details[] list) when a flow has more than one
    active deployment and no slug was supplied.
    MCP (@walkeros/mcp) breaking:
  • deploy_manage's get, delete, and list actions now take
    { projectId?, flowId, slug? }. The old id parameter has been removed.
    flowId is required for get/delete and optional for list. Soft-deleted
    deployments are always excluded.
  • When a flow has multiple active deployments and slug is not provided,
    get/delete return a MULTIPLE_DEPLOYMENTS error with a details[] list
    of { slug, type, status, updatedAt } entries so the caller can pick one.
    deploy action is unchanged.

Event model v4: breaking changes to the Event, Source, and Entity shapes.

  • event.id is now a W3C span_id (16 lowercase hex chars), generated by the
    collector. Reference: W3C Trace Context (W3C Recommendation, January 2020).
  • event.version, event.group, event.count are removed.
  • source.type is now the source kind (e.g. browser, gtag, mcp, cli).
    New source.platform holds the runtime (web | server | app | ...).
  • source.id and source.previous_id are removed.
  • Browser source now sets source.url and source.referrer.
  • MCP source sets source.tool per emission. CLI source sets source.command.
  • Entity.nested and Entity.context are now optional. Root event.nested and
    event.context remain required.
  • Each source self-registers via TypeScript module augmentation of SourceMap
    in @walkeros/core.
  • App-side coordination (/workspaces/developer/app) is a follow-up plan, not
    part of this release. Telemetry from v4 CLI/MCP will not validate against the
    existing app schema until that follow-up ships.
  • Mapping.Rule.skip is renamed to Mapping.Rule.silent. Customer flow.json
    configs using skip: true in mapping rules must rename to silent: true.
    Hard cut: no legacy alias, the field is gone.

Add CodeDiff atom and CodeDiffBox molecule — read-only, theme-aware Monaco
DiffEditor wrappers for side-by-side / inline code diff viewing. CodeDiffBox
mirrors CodeBox's API (header, actions, traffic lights, footer) and adds an
opt-in summary strip, split/inline toggle, and copy button. Supports any Monaco
language; walkerOS $var: / $secret: decorations are applied to both sides
automatically.

IntelliSense improvements for flow.json editors:

  • Chain references (next / before) now autocomplete in all forms: scalar,
    inline array, multi-line array, and Route[] inner next. Previously only the
    scalar form triggered.
  • $store. completions, hover, and validation added. Fed by a new optional
    stores field on IntelliSenseContext; the flow extractor collects store IDs
    from the active flow.
  • $env. completions and hover added. Optional envNames inventory on
    IntelliSenseContext enables validation; when absent, $env. still gets a
    generic hover.
  • $contract. completion now only triggers when the cursor starts a new string
    value, matching runtime semantics (whole-string refs only).
  • package completion detection is JSON-path aware — multi-line "package":
    values now surface completions.
  • Variables and definitions are collected at config / flow / step levels with
    correct cascade priority (step > flow > config).
  • Markers validate chain references in all forms via a JSON walk instead of a
    scalar-only regex.
  • Internals now import the shared REF_* regex constants from @walkeros/core
    — single source of truth, no inline duplicates.

PropertyTable — responsive card-view fallback via CSS container queries
(triggered below 420px), graceful empty-state rendering with an optional
emptyMessage prop (default: "No specific properties available."), and improved
column-width handling so the Description column no longer forces horizontal
overflow in narrow containers.

Add Monaco IntelliSense for $flow.X cross-flow references in Code/CodeBox.
Completion offers known sibling flow names from the parsed flow document, hover
describes the resolved target, decorations style matches the other reference
prefixes, and unknown flow names emit a warning marker. Re-export REF_FLOW
from @walkeros/core so consumers can build inline regex tooling without
reaching into the subpath.

Flow v4: type redesign and cross-flow references.
Breaking changes:

  • Renamed Flow.Settings (single-flow shape) to Flow. The new Flow.Settings
    is the arbitrary kv-bag inside Flow.Config (matches Destination.Settings
    semantics).
  • Renamed Flow.Config (root file shape) to Flow.Json.
  • Removed Flow.Web and Flow.Server. Replaced by
    config.platform: 'web' | 'server' (a string discriminator).
  • Renamed Flow.InlineCode to Flow.Code.
  • Renamed Flow.SourceReference / DestinationReference /
    TransformerReference / StoreReference to Flow.Source / Destination /
    Transformer / Store (Reference suffix dropped).
  • Renamed Flow.ContractEntry to Flow.ContractRule.
  • Lifted bundle and platform fields into the per-flow config block.
  • flow.json version bumped from 3 to 4. v3 input is rejected (no compat
    shim).
    New:
  • $flow.X.Y reference resolves to flows.X.config.Y in the same file. Useful
    for linking a web flow's API destination to a server flow's deployed URL
    without duplicating values.
  • Per-flow Flow.Config block: { platform, url, settings, bundle }.
  • walkeros validate warns on unresolved $flow.X.Y (use --strict to error).
    walkeros bundle and walkeros deploy always error on unresolved refs.
  • See docs/migrating/v3-to-v4.mdx on the website for the manual migration
    steps. No automated codemod is shipped.

Add flowCanvasResult helper + FlowCanvasToolResult / FlowCanvasPayload /
SuggestionTile types for UI-renderable tool outputs. flow_manage actions
get / create / update now return a kind: 'flow-canvas' payload with
optional suggestion tiles so chat clients can render the flow graph inline.

Split @walkeros/mcp into a library entry (server factory plus ToolClient
abstraction) and a thin stdio binary. The package now exports
createWalkerOSMcpServer, HttpToolClient, createStreamableHttpHandler,
TOOL_DEFINITIONS, and the ToolClient interface so host applications can
mount walkerOS MCP tools over HTTP (e.g. from a Next.js Route Handler) or
consume them directly from non-MCP runtimes (e.g. Vercel AI SDK adapters). The
walkeros-mcp stdio binary is unchanged for end users; importing
@walkeros/mcp programmatically no longer auto-starts stdio.

Tool handlers now wrap user-writable strings (flow/project names, config fields,
validation messages) in <user_data>…</user_data> delimiters so chat consumers
can keep prompt-injection defence-in-depth. Two new utilities are exported:
wrapUserData(s) and redactNestedStrings(obj).

Breaking — @walkeros/core: fetchPackage(name, { baseUrl }) now expects
the host app to expose the v2 /api/packages/[name] endpoint that returns the
merged WalkerOSPackage shape directly (single round-trip, ?expand=all). The
previous two-fetch pattern (?path=package.json + ?path=dist/walkerOS.json)
is removed. Hosts must serve the v2 shape; the offline jsdelivr fallback is
unchanged.
Feature — CLI/MCP/explorer: Outbound walkerOS-aware HTTP clients now
identify themselves to the configured app origin via
X-Walkeros-Client: walkeros-{cli|mcp}/{version}. @walkeros/explorer exports
setPackageTypesBaseUrl(url?) so host apps can proxy .d.ts through their own
origin (used by the walkerOS Tag Manager app to drop the jsdelivr CDN allowance
entirely).

BREAKING: Unified reference syntax: $store:id and $secret:NAME now use
the dot separator: $store.id and $secret.NAME.
The coherent rule across every walkerOS reference is:

  • . key or path (resolver looks up or walks what follows)
  • : literal value or raw-code payload (resolver uses what follows
    verbatim)
    $var., $def., $env.NAME[:default], $contract., and $code:(…) are
    unchanged, they already fit the rule.
    Every shipped example, published walkerOS.json metadata, doc page, and skill
    has been updated. A new canonical reference-syntax guide lives at
    /docs/guides/reference-syntax. Regex constants (REF_VAR, REF_DEF,
    REF_ENV, REF_CONTRACT, REF_STORE, REF_SECRET, REF_CODE_PREFIX) are
    exported from @walkeros/core import these instead of hand-rolling regexes.

Upgrade

Search-and-replace across your flow configs:

$store:<id>      → $store.<id>
$secret:<NAME>   → $secret.<NAME>

Everything else stays the same. Your $var.*, $def.*, $env.*,
$contract.*, and $code:* references need no changes.

JSON Schema exports now include canonical id + title (e.g.
Destination.Config, Logger.Config) — replaces anonymous __schema0 /
object / any labels. Extracts shared LoggerConfigSchema (was inlined 5×).
Removes deprecated schemas/value-config.ts. No TypeScript surface changes.

BREAKING: Unified callback signatures across mapping and on.*
subscriptions.
Every callback in walkerOS now reads (data, context) => result. Sources,
transformers, destinations, and stores already conformed; mapping and on.* join
the family in v4.1.

Mapping callbacks

fn, condition, and validate now share a single shape:
(value, context: Mapping.Context) => result
Mapping.Options is removed. Replaced by Mapping.Context:

interface Context {
  event: WalkerOS.DeepPartialEvent;
  mapping: Value | Rule;
  collector: Collector.Instance; // required
  logger: Logger.Instance; // required
  consent?: WalkerOS.Consent; // resolved consent
}

Rule-level condition is now (event, context) => boolean.
Mapping.Options.props is removed (no production callers).

Mapping upgrade

// before
const fn: Mapping.Fn = (value, mapping, options) => /* … */;
const cond: Mapping.Condition = (value, mapping, collector) => /* … */;
const val: Mapping.Validate = (value) => /* … */;
// after
const fn: Mapping.Fn = (value, context) => /* … */;
const cond: Mapping.Condition = (value, context) => /* … */;
const val: Mapping.Validate = (value, context) => /* … */;

In $code: strings (flow.json):

// before
"fn": "$code:(value, mapping, options) => …"
"condition": "$code:(value, mapping, collector) => …"
// after
"fn": "$code:(value, context) => …"
"condition": "$code:(value, context) => …"

context.mapping replaces the second positional arg; context.collector,
context.logger, and context.consent are all available.
One-arg callbacks like (value) => value.toUpperCase() continue to work
unchanged.

On.* subscription callbacks

walker.on('consent', …), walker.on('ready', …), etc. now receive
(data, context: On.Context) => void | Promise<void>. The legacy Context
interface, *Config aliases, and Options discriminated union are removed.

interface Context {
  collector: Collector.Instance; // required
  logger: Logger.Instance; // required
}
type Fn<TData = unknown> = (
  data: TData,
  context: Context,
) => void | Promise<void>;
type ConsentFn = Fn<WalkerOS.Consent>;
type SessionFn = Fn<Collector.SessionData | undefined>;
type UserFn = Fn<WalkerOS.User>;
type ReadyFn = Fn<void>;
type RunFn = Fn<void>;
type GenericFn = Fn<unknown>;

The new On.Subscription alias is the registerable union for
walker.on(action, X).

On.* upgrade

// before
walker.on('consent', { marketing: (collector, consent) => /* … */ });
walker.on('ready', (collector) => /* … */);
walker.on('session', (collector, session) => /* … */);
// after
walker.on('consent', { marketing: (consent, ctx) => /* … */ });
walker.on('ready', (_, ctx) => /* … */);
walker.on('session', (session, ctx) => /* … */);

ctx.collector replaces the positional first arg; ctx.logger is also
available.

Why both at once

Both refactors follow the same (data, context) pattern. Shipping them in one
release means consumers do one search-and-replace pass instead of two, and the
codebase reaches full callback-signature consistency in v4.1.

Published Packages