Add runtime provenance metadata for alert remediation
This commit is contained in:
parent
598f1826d8
commit
7136087486
@ -492,6 +492,7 @@ function runAgentAttempt(params: {
|
||||
sessionKey: params.sessionKey,
|
||||
agentId: params.sessionAgentId,
|
||||
trigger: "user",
|
||||
runContext: { sessionTarget: "main" },
|
||||
messageChannel: params.messageChannel,
|
||||
agentAccountId: params.runContext.accountId,
|
||||
messageTo: params.opts.replyTo ?? params.opts.to,
|
||||
|
||||
@ -2549,6 +2549,13 @@ export async function runEmbeddedAttempt(
|
||||
sessionKey: sandboxSessionKey,
|
||||
sessionId: params.sessionId,
|
||||
agentId: sessionAgentId,
|
||||
runContext: {
|
||||
sessionTarget:
|
||||
params.runContext?.sessionTarget ??
|
||||
(params.trigger === "cron" ? "isolated" : sandboxSessionKey ? "main" : undefined),
|
||||
cronJobId: params.runContext?.cronJobId,
|
||||
maintenanceScope: params.runContext?.maintenanceScope,
|
||||
},
|
||||
});
|
||||
|
||||
const {
|
||||
|
||||
@ -20,6 +20,12 @@ export type ClientToolDefinition = {
|
||||
};
|
||||
};
|
||||
|
||||
export type RunEmbeddedPiAgentContext = {
|
||||
sessionTarget?: "main" | "isolated";
|
||||
cronJobId?: string;
|
||||
maintenanceScope?: string;
|
||||
};
|
||||
|
||||
export type RunEmbeddedPiAgentParams = {
|
||||
sessionId: string;
|
||||
sessionKey?: string;
|
||||
@ -119,6 +125,7 @@ export type RunEmbeddedPiAgentParams = {
|
||||
streamParams?: AgentStreamParams;
|
||||
ownerNumbers?: string[];
|
||||
enforceFinalTag?: boolean;
|
||||
runContext?: RunEmbeddedPiAgentContext;
|
||||
/**
|
||||
* Allow a single run attempt even when all auth profiles are in cooldown,
|
||||
* but only for inferred transient cooldowns like `rate_limit` or `overloaded`.
|
||||
|
||||
@ -9,13 +9,22 @@ vi.mock("../infra/agent-events.js", () => ({
|
||||
|
||||
function createContext(
|
||||
lastAssistant: unknown,
|
||||
overrides?: { onAgentEvent?: (event: unknown) => void },
|
||||
overrides?: {
|
||||
onAgentEvent?: (event: unknown) => void;
|
||||
sessionKey?: string;
|
||||
runContext?: {
|
||||
sessionTarget?: "main" | "isolated";
|
||||
cronJobId?: string;
|
||||
maintenanceScope?: string;
|
||||
};
|
||||
},
|
||||
): EmbeddedPiSubscribeContext {
|
||||
return {
|
||||
params: {
|
||||
runId: "run-1",
|
||||
config: {},
|
||||
sessionKey: "agent:main:main",
|
||||
sessionKey: overrides?.sessionKey ?? "agent:main:main",
|
||||
runContext: overrides?.runContext,
|
||||
onAgentEvent: overrides?.onAgentEvent,
|
||||
},
|
||||
state: {
|
||||
@ -60,6 +69,8 @@ describe("handleAgentEnd", () => {
|
||||
runId: "run-1",
|
||||
error: "connection refused",
|
||||
rawErrorPreview: "connection refused",
|
||||
sessionTarget: "main",
|
||||
sessionKey: "agent:main:main",
|
||||
});
|
||||
expect(onAgentEvent).toHaveBeenCalledWith({
|
||||
stream: "lifecycle",
|
||||
@ -157,4 +168,38 @@ describe("handleAgentEnd", () => {
|
||||
expect(ctx.log.warn).not.toHaveBeenCalled();
|
||||
expect(ctx.log.debug).toHaveBeenCalledWith("embedded run agent end: runId=run-1 isError=false");
|
||||
});
|
||||
|
||||
it("includes explicit provenance metadata in error logs when provided by the runtime", () => {
|
||||
const ctx = createContext(
|
||||
{
|
||||
role: "assistant",
|
||||
stopReason: "error",
|
||||
errorMessage: "provider overloaded",
|
||||
content: [{ type: "text", text: "" }],
|
||||
},
|
||||
{
|
||||
sessionKey: "agent:main:cron:job-1",
|
||||
runContext: {
|
||||
sessionTarget: "isolated",
|
||||
cronJobId: "job-1",
|
||||
maintenanceScope: "watchdog",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
handleAgentEnd(ctx);
|
||||
|
||||
expect(vi.mocked(ctx.log.warn).mock.calls[0]?.[1]).toMatchObject({
|
||||
sessionTarget: "isolated",
|
||||
sessionKey: "agent:main:cron:job-1",
|
||||
cronJobId: "job-1",
|
||||
maintenanceScope: "watchdog",
|
||||
runContext: {
|
||||
sessionTarget: "isolated",
|
||||
sessionKey: "agent:main:cron:job-1",
|
||||
cronJobId: "job-1",
|
||||
maintenanceScope: "watchdog",
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { emitAgentEvent } from "../infra/agent-events.js";
|
||||
import { createInlineCodeState } from "../markdown/code-spans.js";
|
||||
import { isCronSessionKey } from "../sessions/session-key-utils.js";
|
||||
import {
|
||||
buildApiErrorObservationFields,
|
||||
buildTextObservationFields,
|
||||
@ -33,6 +34,14 @@ export function handleAgentStart(ctx: EmbeddedPiSubscribeContext) {
|
||||
export function handleAgentEnd(ctx: EmbeddedPiSubscribeContext) {
|
||||
const lastAssistant = ctx.state.lastAssistant;
|
||||
const isError = isAssistantMessage(lastAssistant) && lastAssistant.stopReason === "error";
|
||||
const runContext = ctx.params.runContext ?? {};
|
||||
const sessionTarget =
|
||||
runContext.sessionTarget ??
|
||||
(isCronSessionKey(ctx.params.sessionKey)
|
||||
? "isolated"
|
||||
: ctx.params.sessionKey
|
||||
? "main"
|
||||
: undefined);
|
||||
|
||||
if (isError && lastAssistant) {
|
||||
const friendlyError = formatAssistantErrorText(lastAssistant, {
|
||||
@ -59,6 +68,16 @@ export function handleAgentEnd(ctx: EmbeddedPiSubscribeContext) {
|
||||
failoverReason,
|
||||
model: lastAssistant.model,
|
||||
provider: lastAssistant.provider,
|
||||
sessionTarget,
|
||||
sessionKey: ctx.params.sessionKey,
|
||||
cronJobId: runContext.cronJobId,
|
||||
maintenanceScope: runContext.maintenanceScope,
|
||||
runContext: {
|
||||
sessionTarget,
|
||||
sessionKey: ctx.params.sessionKey,
|
||||
cronJobId: runContext.cronJobId,
|
||||
maintenanceScope: runContext.maintenanceScope,
|
||||
},
|
||||
...observedError,
|
||||
consoleMessage: `embedded run agent end: runId=${safeRunId} isError=true model=${safeModel} provider=${safeProvider} error=${safeErrorText}`,
|
||||
});
|
||||
|
||||
@ -36,6 +36,11 @@ export type SubscribeEmbeddedPiSessionParams = {
|
||||
sessionId?: string;
|
||||
/** Agent identity for hook context — resolved from session config in attempt.ts. */
|
||||
agentId?: string;
|
||||
runContext?: {
|
||||
sessionTarget?: "main" | "isolated";
|
||||
cronJobId?: string;
|
||||
maintenanceScope?: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type { BlockReplyChunking } from "./pi-embedded-block-chunker.js";
|
||||
|
||||
@ -327,6 +327,7 @@ export async function runAgentTurnWithFallback(params: {
|
||||
...embeddedContext,
|
||||
allowGatewaySubagentBinding: true,
|
||||
trigger: params.isHeartbeat ? "heartbeat" : "user",
|
||||
runContext: { sessionTarget: "main" },
|
||||
groupId: resolveGroupSessionKey(params.sessionCtx)?.id,
|
||||
groupChannel:
|
||||
params.sessionCtx.GroupChannel?.trim() ?? params.sessionCtx.GroupSubject?.trim(),
|
||||
|
||||
@ -496,6 +496,7 @@ export async function runMemoryFlushIfNeeded(params: {
|
||||
...runBaseParams,
|
||||
allowGatewaySubagentBinding: true,
|
||||
trigger: "memory",
|
||||
runContext: { maintenanceScope: "memory-flush" },
|
||||
memoryFlushWritePath,
|
||||
prompt: resolveMemoryFlushPromptForRun({
|
||||
prompt: memoryFlushSettings.prompt,
|
||||
|
||||
@ -179,6 +179,7 @@ export function createFollowupRunner(params: {
|
||||
sessionKey: queued.run.sessionKey,
|
||||
agentId: queued.run.agentId,
|
||||
trigger: "user",
|
||||
runContext: { sessionTarget: "main" },
|
||||
messageChannel: queued.originatingChannel ?? undefined,
|
||||
messageProvider: queued.run.messageProvider,
|
||||
agentAccountId: queued.run.agentAccountId,
|
||||
|
||||
@ -659,6 +659,10 @@ export async function runCronIsolatedAgentTurn(params: {
|
||||
bootstrapContextMode: agentPayload?.lightContext ? "lightweight" : undefined,
|
||||
bootstrapContextRunKind: "cron",
|
||||
runId: cronSession.sessionEntry.sessionId,
|
||||
runContext: {
|
||||
sessionTarget: "isolated",
|
||||
cronJobId: params.job.id,
|
||||
},
|
||||
requireExplicitMessageTarget: toolPolicy.requireExplicitMessageTarget,
|
||||
disableMessageTool: toolPolicy.disableMessageTool,
|
||||
allowTransientCooldownProbe: runOptions?.allowTransientCooldownProbe,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user