diff --git a/apps/cli/src/main.ts b/apps/cli/src/main.ts index 0192820eb..0c786464e 100644 --- a/apps/cli/src/main.ts +++ b/apps/cli/src/main.ts @@ -394,6 +394,7 @@ const executeCode = (input: { payload: { code: input.code, }, + headers: { "x-executor-trigger": "cli" }, }); if (response.status === "paused") { diff --git a/packages/core/api/src/executions/api.ts b/packages/core/api/src/executions/api.ts index 12b557919..d92332435 100644 --- a/packages/core/api/src/executions/api.ts +++ b/packages/core/api/src/executions/api.ts @@ -11,6 +11,16 @@ const ExecuteRequest = Schema.Struct({ code: Schema.String, }); +/** + * Optional header naming the surface that triggered this execution — + * `"cli"`, `"http"`, `"mcp"`, etc. Persisted on the execution row so + * the runs UI can facet by trigger kind. Defaults to `"http"` when + * absent. + */ +const ExecuteHeaders = Schema.Struct({ + "x-executor-trigger": Schema.optional(Schema.String), +}); + const CompletedResult = Schema.Struct({ status: Schema.Literal("completed"), text: Schema.String, @@ -55,6 +65,7 @@ export class ExecutionsApi extends HttpApiGroup.make("executions") .add( HttpApiEndpoint.post("execute")`/executions` .setPayload(ExecuteRequest) + .setHeaders(ExecuteHeaders) .addSuccess(ExecuteResponse), ) .add( diff --git a/packages/core/api/src/handlers/executions.ts b/packages/core/api/src/handlers/executions.ts index 3eedd3e65..82fc2ea39 100644 --- a/packages/core/api/src/handlers/executions.ts +++ b/packages/core/api/src/handlers/executions.ts @@ -8,10 +8,15 @@ import { capture, captureEngineError } from "@executor/api"; export const ExecutionsHandlers = HttpApiBuilder.group(ExecutorApi, "executions", (handlers) => handlers - .handle("execute", ({ payload }) => + .handle("execute", ({ payload, headers }) => capture(Effect.gen(function* () { const engine = yield* ExecutionEngineService; - const outcome = yield* captureEngineError(engine.executeWithPause(payload.code)); + const triggerKind = headers["x-executor-trigger"] ?? "http"; + const outcome = yield* captureEngineError( + engine.executeWithPause(payload.code, { + trigger: { kind: triggerKind }, + }), + ); if (outcome.status === "completed") { const formatted = formatExecuteResult(outcome.result); diff --git a/packages/hosts/mcp/src/server.ts b/packages/hosts/mcp/src/server.ts index 4c30abbb5..894ec9cc0 100644 --- a/packages/hosts/mcp/src/server.ts +++ b/packages/hosts/mcp/src/server.ts @@ -297,10 +297,13 @@ export const createExecutorMcpServer = ( if (supportsManagedElicitation(server)) { const result = yield* engine.execute(code, { onElicitation: makeMcpElicitationHandler(server, debugLog), + trigger: { kind: "mcp" }, }); return toMcpResult(formatExecuteResult(result)); } - const outcome = yield* engine.executeWithPause(code); + const outcome = yield* engine.executeWithPause(code, { + trigger: { kind: "mcp" }, + }); debugLog("execute.paused_flow_result", { status: outcome.status, executionId: outcome.status === "paused" ? outcome.execution.id : undefined,