diff --git a/src/agents/cli-runner.before-agent-reply-cron.test.ts b/src/agents/cli-runner.before-agent-reply-cron.test.ts index 2221a32880a..dcbf1f1e3a5 100644 --- a/src/agents/cli-runner.before-agent-reply-cron.test.ts +++ b/src/agents/cli-runner.before-agent-reply-cron.test.ts @@ -108,12 +108,13 @@ describe("runCliAgent cron before_agent_reply seam", () => { reply: { text: "dreaming claimed via cli runner" }, }); - const result = await runCliAgent({ ...baseRunParams, trigger: "cron" }); + const result = await runCliAgent({ ...baseRunParams, trigger: "cron", jobId: "cron-job-123" }); expect(runBeforeAgentReplyMock).toHaveBeenCalledTimes(1); expect(runBeforeAgentReplyMock).toHaveBeenCalledWith( { cleanedBody: baseRunParams.prompt }, expect.objectContaining({ + jobId: "cron-job-123", agentId: baseRunParams.agentId, sessionId: baseRunParams.sessionId, sessionKey: baseRunParams.sessionKey, @@ -133,7 +134,7 @@ describe("runCliAgent cron before_agent_reply seam", () => { hasHooksMock.mockImplementation((hookName) => hookName === "before_agent_reply"); runBeforeAgentReplyMock.mockResolvedValue({ handled: true }); - await runCliAgent({ ...baseRunParams, trigger: "cron" }); + await runCliAgent({ ...baseRunParams, trigger: "cron", jobId: "cron-job-123" }); expect(prepareCliRunContextMock).not.toHaveBeenCalled(); expect(executePreparedCliRunMock).not.toHaveBeenCalled(); @@ -144,7 +145,7 @@ describe("runCliAgent cron before_agent_reply seam", () => { hasHooksMock.mockImplementation((hookName) => hookName === "before_agent_reply"); runBeforeAgentReplyMock.mockResolvedValue({ handled: true }); - const result = await runCliAgent({ ...baseRunParams, trigger: "cron" }); + const result = await runCliAgent({ ...baseRunParams, trigger: "cron", jobId: "cron-job-123" }); expect(executePreparedCliRunMock).not.toHaveBeenCalled(); expect(result.payloads?.[0]?.text).toBe(SILENT_REPLY_TOKEN); diff --git a/src/agents/cli-runner.spawn.test.ts b/src/agents/cli-runner.spawn.test.ts index f527d329d35..dc32f042d40 100644 --- a/src/agents/cli-runner.spawn.test.ts +++ b/src/agents/cli-runner.spawn.test.ts @@ -498,6 +498,22 @@ describe("runCliAgent spawn path", () => { expect(params.extraSystemPromptStatic).toBe("static"); }); + it("forwards cron jobId through the compat wrapper", () => { + const params = buildRunClaudeCliAgentParams({ + sessionId: "openclaw-session", + sessionFile: "/tmp/session.jsonl", + workspaceDir: "/tmp", + prompt: "hi", + timeoutMs: 1_000, + runId: "run-claude-jobid-wrapper", + trigger: "cron", + jobId: "cron-job-123", + }); + + expect(params.trigger).toBe("cron"); + expect(params.jobId).toBe("cron-job-123"); + }); + it("runs CLI through supervisor and returns payload", async () => { supervisorSpawnMock.mockResolvedValueOnce( createManagedRun({ diff --git a/src/agents/cli-runner.ts b/src/agents/cli-runner.ts index 3984b408282..6645ed345e2 100644 --- a/src/agents/cli-runner.ts +++ b/src/agents/cli-runner.ts @@ -70,6 +70,7 @@ export async function runCliAgent(params: RunCliAgentParams): Promise { const result = await runEmbeddedPiAgent({ ...overflowBaseRunParams, trigger: "cron", + jobId: "cron-job-123", prompt: "__openclaw_memory_core_short_term_promotion_dream__", }); expect(mockedGlobalHookRunner.runBeforeAgentReply).toHaveBeenCalledWith( { cleanedBody: "__openclaw_memory_core_short_term_promotion_dream__" }, expect.objectContaining({ + jobId: "cron-job-123", agentId: "main", sessionId: "test-session", sessionKey: "test-key", diff --git a/src/agents/pi-embedded-runner/run.ts b/src/agents/pi-embedded-runner/run.ts index d7de8ec7481..78305ab4b03 100644 --- a/src/agents/pi-embedded-runner/run.ts +++ b/src/agents/pi-embedded-runner/run.ts @@ -326,6 +326,7 @@ export async function runEmbeddedPiAgent( const hookRunner = getGlobalHookRunner(); const hookCtx = { runId: params.runId, + jobId: params.jobId, agentId: workspaceResolution.agentId, sessionKey: resolvedSessionKey, sessionId: params.sessionId, diff --git a/src/agents/pi-embedded-runner/run/params.ts b/src/agents/pi-embedded-runner/run/params.ts index 2278766d0d9..73a2f003758 100644 --- a/src/agents/pi-embedded-runner/run/params.ts +++ b/src/agents/pi-embedded-runner/run/params.ts @@ -31,6 +31,8 @@ export type RunEmbeddedPiAgentParams = { agentAccountId?: string; /** What initiated this agent run: "user", "heartbeat", "cron", "memory", "overflow", or "manual". */ trigger?: EmbeddedRunTrigger; + /** Stable cron job identifier populated for cron-triggered runs. */ + jobId?: string; /** Relative workspace path that memory-triggered writes are allowed to append to. */ memoryFlushWritePath?: string; /** Delivery target for topic/thread routing. */ diff --git a/src/cron/isolated-agent/run-executor.ts b/src/cron/isolated-agent/run-executor.ts index 385301a1adf..2941c39835e 100644 --- a/src/cron/isolated-agent/run-executor.ts +++ b/src/cron/isolated-agent/run-executor.ts @@ -130,6 +130,7 @@ export function createCronPromptExecutor(params: { sessionKey: params.agentSessionKey, agentId: params.agentId, trigger: "cron", + jobId: params.job.id, sessionFile, workspaceDir: params.workspaceDir, config: params.cfgWithAgentDefaults, @@ -163,6 +164,7 @@ export function createCronPromptExecutor(params: { sessionKey: params.agentSessionKey, agentId: params.agentId, trigger: "cron", + jobId: params.job.id, cleanupBundleMcpOnRunEnd: params.job.sessionTarget === "isolated", allowGatewaySubagentBinding: true, senderIsOwner: false, diff --git a/src/plugins/hook-types.ts b/src/plugins/hook-types.ts index d96e7f6266e..98d242b0083 100644 --- a/src/plugins/hook-types.ts +++ b/src/plugins/hook-types.ts @@ -161,6 +161,7 @@ export const isConversationHookName = (hookName: PluginHookName): boolean => export type PluginHookAgentContext = { runId?: string; + jobId?: string; trace?: DiagnosticTraceContext; agentId?: string; sessionKey?: string;