Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
a333857
feat(sdk): add execution history types and ExecutionStore interface
aryasaatvik Apr 11, 2026
0364652
feat(storage): add ExecutionStore implementations for SQLite and Post…
aryasaatvik Apr 11, 2026
49dacda
feat(execution): add execution history persistence to engine
aryasaatvik Apr 11, 2026
c9016f1
feat(sdk): integrate ExecutionStore into executor lifecycle
aryasaatvik Apr 11, 2026
d1fc4c0
feat(react): add execution history page with filters, timeline, and d…
aryasaatvik Apr 11, 2026
6255415
feat(app): add /runs routes and sidebar navigation
aryasaatvik Apr 11, 2026
55159fc
feat(core/sdk): add tool call tracking to execution store
aryasaatvik Apr 11, 2026
1f774ce
feat(storage): add execution_tool_calls tracking
aryasaatvik Apr 11, 2026
7890e2e
feat(core/execution): record tool calls and trigger kind per executio…
aryasaatvik Apr 11, 2026
c4bf0ba
feat(core/api): add trigger metadata and tool call tracking to execut…
aryasaatvik Apr 11, 2026
ea8b212
feat(execution): add trigger context propagation from CLI and MCP hosts
aryasaatvik Apr 11, 2026
1850ed9
feat(react): add tool calls display, facet filters, and live mode to …
aryasaatvik Apr 11, 2026
cdbd8d5
feat(app): add trigger/tool/live filter params to /runs routes
aryasaatvik Apr 11, 2026
fa08d41
chore(deps): add @date-fns/utc and react-hotkeys-hook
aryasaatvik Apr 11, 2026
535bb62
chore: remove personal MCP server config
aryasaatvik Apr 11, 2026
86f6b49
feat(executions): add sort and elicitation filters to execution list
aryasaatvik Apr 11, 2026
e594ce1
refactor(execution): unify mcp trigger kinds to single "mcp" value
aryasaatvik Apr 11, 2026
26502e1
feat(storage): add hadElicitation filter, interactionCounts meta, and…
aryasaatvik Apr 11, 2026
e7782de
refactor(runs): drop mcp-inline/mcp-pause split, use single mcp kind
aryasaatvik Apr 11, 2026
5687f0b
feat(runs): add sortable columns and interactions elicitation filter
aryasaatvik Apr 11, 2026
d062f97
feat(app): add sort and elicitation filter params to /runs routes
aryasaatvik Apr 11, 2026
9367681
style: use vitesse themes instead of github themes for Shiki
aryasaatvik Apr 11, 2026
dd2a775
style(runs): reformat execution and runs UI files
aryasaatvik Apr 11, 2026
e4f2732
aim(runs): align live mode updates with useEffectEvent
aryasaatvik Apr 11, 2026
ba1f078
Merge branch 'main' into execution-history-runs
aryasaatvik Apr 12, 2026
7b4102b
style(runs): strip comment noise and section dividers
aryasaatvik Apr 12, 2026
1253842
refactor(runs): extract shared cursor utils, deduplicate formatTimest…
aryasaatvik Apr 12, 2026
e5a3c29
refactor(runs): inline single-use helpers
aryasaatvik Apr 12, 2026
32e9c06
feat(kernel/core): add error message formatting utilities
aryasaatvik Apr 12, 2026
01b34bc
feat(execution-core): add OpenTelemetry metrics and span annotations
aryasaatvik Apr 12, 2026
3f3b349
refactor(kernel): add tracing spans to all runtime executors
aryasaatvik Apr 12, 2026
1b13ba0
refactor(cloud/api): migrate to Effect logging and wire TelemetryLive
aryasaatvik Apr 12, 2026
f4e7596
docs(telemetry): clarify tracing scope and document metrics export li…
aryasaatvik Apr 12, 2026
358cd45
docs(runs): clarify why JSON unwrapping is needed for QuickJS
aryasaatvik Apr 12, 2026
fea0149
chore(execution): clarify user-facing tool-call recording behavior
aryasaatvik Apr 12, 2026
468cc9b
merge: main into execution-history-runs
aryasaatvik Apr 24, 2026
b234187
chore(storage-postgres): drop leftover pre-DBAdapter drizzle artifacts
aryasaatvik Apr 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/cli/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,7 @@ const executeCode = (input: {
payload: {
code: input.code,
},
headers: { "x-executor-trigger": "cli" },
});

if (response.status === "paused") {
Expand Down
32 changes: 30 additions & 2 deletions apps/cloud/src/api/autumn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,31 @@ import { HttpServerRequest, HttpServerResponse } from "@effect/platform";
import { autumnHandler } from "autumn-js/backend";

import { WorkOSAuth } from "../auth/workos";
import { AutumnService } from "../services/autumn";
import { HttpResponseError, isServerError, toErrorServerResponse } from "./error-response";
import { SharedServices } from "./layers";

export const makeTrackExecutionUsage = (autumn: AutumnService["Type"]) => {
return (organizationId: string): void => {
autumn
.use((client) =>
client.track({
customerId: organizationId,
featureId: "executions",
value: 1,
}),
)
.pipe(
Effect.catchAll((err) =>
Effect.logError("[billing] track failed").pipe(
Effect.annotateLogs("error", String(err)),
),
),
Effect.runFork,
);
};
};

export const AutumnApiApp = Effect.gen(function* () {
const request = yield* HttpServerRequest.HttpServerRequest;
const webRequest = yield* Effect.mapError(
Expand Down Expand Up @@ -66,7 +88,10 @@ export const AutumnApiApp = Effect.gen(function* () {
);

if (statusCode >= 400) {
console.error("[autumn] upstream error:", statusCode, response);
yield* Effect.logWarning("[autumn] upstream error").pipe(
Effect.annotateLogs("statusCode", statusCode),
Effect.annotateLogs("response", JSON.stringify(response)),
);
return yield* Effect.fail(
new HttpResponseError({
status: statusCode,
Expand All @@ -81,7 +106,10 @@ export const AutumnApiApp = Effect.gen(function* () {
Effect.provide(SharedServices),
Effect.catchAll((err) => {
if (isServerError(err)) {
console.error("[autumn] request failed:", err instanceof Error ? err.stack : err);
return Effect.logError("[autumn] request failed").pipe(
Effect.annotateLogs("error", err instanceof Error ? err.stack ?? err.message : String(err)),
Effect.map(() => toErrorServerResponse(err)),
);
}
return Effect.succeed(toErrorServerResponse(err));
}),
Expand Down
5 changes: 4 additions & 1 deletion apps/cloud/src/api/protected.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,10 @@ export const ProtectedApiApp = Effect.gen(function* () {
Effect.provide(SharedServices),
Effect.catchAll((err) => {
if (isServerError(err)) {
console.error("[api] request failed:", err instanceof Error ? err.stack : err);
return Effect.logError("[api] request failed").pipe(
Effect.annotateLogs("error", err instanceof Error ? err.stack ?? err.message : String(err)),
Effect.map(() => toErrorServerResponse(err)),
);
}
return Effect.succeed(toErrorServerResponse(err));
}),
Expand Down
21 changes: 21 additions & 0 deletions apps/cloud/src/routeTree.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import { Route as rootRouteImport } from './routes/__root'
import { Route as ToolsRouteImport } from './routes/tools'
import { Route as SecretsRouteImport } from './routes/secrets'
import { Route as RunsRouteImport } from './routes/runs'
import { Route as OrgRouteImport } from './routes/org'
import { Route as ConnectionsRouteImport } from './routes/connections'
import { Route as BillingRouteImport } from './routes/billing'
Expand All @@ -29,6 +30,11 @@ const SecretsRoute = SecretsRouteImport.update({
path: '/secrets',
getParentRoute: () => rootRouteImport,
} as any)
const RunsRoute = RunsRouteImport.update({
id: '/runs',
path: '/runs',
getParentRoute: () => rootRouteImport,
} as any)
const OrgRoute = OrgRouteImport.update({
id: '/org',
path: '/org',
Expand Down Expand Up @@ -70,6 +76,7 @@ export interface FileRoutesByFullPath {
'/billing': typeof BillingRoute
'/connections': typeof ConnectionsRoute
'/org': typeof OrgRoute
'/runs': typeof RunsRoute
'/secrets': typeof SecretsRoute
'/tools': typeof ToolsRoute
'/billing/plans': typeof BillingPlansRoute
Expand All @@ -81,6 +88,7 @@ export interface FileRoutesByTo {
'/billing': typeof BillingRoute
'/connections': typeof ConnectionsRoute
'/org': typeof OrgRoute
'/runs': typeof RunsRoute
'/secrets': typeof SecretsRoute
'/tools': typeof ToolsRoute
'/billing/plans': typeof BillingPlansRoute
Expand All @@ -93,6 +101,7 @@ export interface FileRoutesById {
'/billing': typeof BillingRoute
'/connections': typeof ConnectionsRoute
'/org': typeof OrgRoute
'/runs': typeof RunsRoute
'/secrets': typeof SecretsRoute
'/tools': typeof ToolsRoute
'/billing_/plans': typeof BillingPlansRoute
Expand All @@ -106,6 +115,7 @@ export interface FileRouteTypes {
| '/billing'
| '/connections'
| '/org'
| '/runs'
| '/secrets'
| '/tools'
| '/billing/plans'
Expand All @@ -117,6 +127,7 @@ export interface FileRouteTypes {
| '/billing'
| '/connections'
| '/org'
| '/runs'
| '/secrets'
| '/tools'
| '/billing/plans'
Expand All @@ -128,6 +139,7 @@ export interface FileRouteTypes {
| '/billing'
| '/connections'
| '/org'
| '/runs'
| '/secrets'
| '/tools'
| '/billing_/plans'
Expand All @@ -140,6 +152,7 @@ export interface RootRouteChildren {
BillingRoute: typeof BillingRoute
ConnectionsRoute: typeof ConnectionsRoute
OrgRoute: typeof OrgRoute
RunsRoute: typeof RunsRoute
SecretsRoute: typeof SecretsRoute
ToolsRoute: typeof ToolsRoute
BillingPlansRoute: typeof BillingPlansRoute
Expand All @@ -163,6 +176,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof SecretsRouteImport
parentRoute: typeof rootRouteImport
}
'/runs': {
id: '/runs'
path: '/runs'
fullPath: '/runs'
preLoaderRoute: typeof RunsRouteImport
parentRoute: typeof rootRouteImport
}
'/org': {
id: '/org'
path: '/org'
Expand Down Expand Up @@ -220,6 +240,7 @@ const rootRouteChildren: RootRouteChildren = {
BillingRoute: BillingRoute,
ConnectionsRoute: ConnectionsRoute,
OrgRoute: OrgRoute,
RunsRoute: RunsRoute,
SecretsRoute: SecretsRoute,
ToolsRoute: ToolsRoute,
BillingPlansRoute: BillingPlansRoute,
Expand Down
24 changes: 24 additions & 0 deletions apps/cloud/src/routes/runs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Schema } from "effect";
import { createFileRoute } from "@tanstack/react-router";
import { RunsPage, type RunsSearch } from "@executor/react/pages/runs";

const RunsSearchSchema = Schema.standardSchemaV1(
Schema.Struct({
executionId: Schema.optional(Schema.String),
status: Schema.optional(Schema.String),
trigger: Schema.optional(Schema.String),
tool: Schema.optional(Schema.String),
range: Schema.optional(Schema.String),
from: Schema.optional(Schema.String),
to: Schema.optional(Schema.String),
code: Schema.optional(Schema.String),
live: Schema.optional(Schema.String),
sort: Schema.optional(Schema.String),
elicitation: Schema.optional(Schema.String),
}),
);

export const Route = createFileRoute("/runs")({
validateSearch: RunsSearchSchema,
component: () => <RunsPage search={Route.useSearch() as RunsSearch} />,
});
2 changes: 2 additions & 0 deletions apps/cloud/src/web/shell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,7 @@ function UserFooter() {
function SidebarContent(props: { pathname: string; onNavigate?: () => void; showBrand?: boolean }) {
const isHome = props.pathname === "/";
const isSecrets = props.pathname === "/secrets";
const isRuns = props.pathname === "/runs";
const isConnections = props.pathname === "/connections";
const isBilling = props.pathname === "/billing" || props.pathname.startsWith("/billing/");
const isOrg = props.pathname === "/org";
Expand All @@ -378,6 +379,7 @@ function SidebarContent(props: { pathname: string; onNavigate?: () => void; show
<NavItem to="/" label="Sources" active={isHome} onNavigate={props.onNavigate} />
<NavItem to="/connections" label="Connections" active={isConnections} onNavigate={props.onNavigate} />
<NavItem to="/secrets" label="Secrets" active={isSecrets} onNavigate={props.onNavigate} />
<NavItem to="/runs" label="Runs" active={isRuns} onNavigate={props.onNavigate} />
<NavItem to="/org" label="Organization" active={isOrg} onNavigate={props.onNavigate} />
<NavItem to="/billing" label="Billing" active={isBilling} onNavigate={props.onNavigate} />

Expand Down
24 changes: 24 additions & 0 deletions apps/local/src/routes/runs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Schema } from "effect";
import { createFileRoute } from "@tanstack/react-router";
import { RunsPage, type RunsSearch } from "@executor/react/pages/runs";

const RunsSearchSchema = Schema.standardSchemaV1(
Schema.Struct({
executionId: Schema.optional(Schema.String),
status: Schema.optional(Schema.String),
trigger: Schema.optional(Schema.String),
tool: Schema.optional(Schema.String),
range: Schema.optional(Schema.String),
from: Schema.optional(Schema.String),
to: Schema.optional(Schema.String),
code: Schema.optional(Schema.String),
live: Schema.optional(Schema.String),
sort: Schema.optional(Schema.String),
elicitation: Schema.optional(Schema.String),
}),
);

export const Route = createFileRoute("/runs")({
validateSearch: RunsSearchSchema,
component: () => <RunsPage search={Route.useSearch() as RunsSearch} />,
});
2 changes: 2 additions & 0 deletions apps/local/src/web/shell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@ function SidebarContent(props: {
}) {
const isHome = props.pathname === "/";
const isSecrets = props.pathname === "/secrets";
const isRuns = props.pathname === "/runs";
const isConnections = props.pathname === "/connections";

return (
Expand All @@ -322,6 +323,7 @@ function SidebarContent(props: {
<NavItem to="/" label="Sources" active={isHome} onNavigate={props.onNavigate} />
<NavItem to="/connections" label="Connections" active={isConnections} onNavigate={props.onNavigate} />
<NavItem to="/secrets" label="Secrets" active={isSecrets} onNavigate={props.onNavigate} />
<NavItem to="/runs" label="Runs" active={isRuns} onNavigate={props.onNavigate} />

{/* Sources list */}
<div className="mt-5 mb-1 px-2.5 text-xs font-medium uppercase tracking-widest text-muted-foreground">
Expand Down
25 changes: 25 additions & 0 deletions packages/core/sdk/src/cursor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type { Execution } from "./executions";
import { ExecutionId } from "./ids";

export const encodeCursor = (execution: Execution): string =>
encodeURIComponent(JSON.stringify({ createdAt: execution.createdAt, id: execution.id }));

export const decodeCursor = (
cursor: string,
): {
readonly createdAt: number;
readonly id: ExecutionId;
} | null => {
try {
const parsed = JSON.parse(decodeURIComponent(cursor)) as {
createdAt?: unknown;
id?: unknown;
};
if (typeof parsed.createdAt !== "number" || typeof parsed.id !== "string") {
return null;
}
return { createdAt: parsed.createdAt, id: ExecutionId.make(parsed.id) };
} catch {
return null;
}
};
Loading
Loading