mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-18 23:24:45 +00:00
fix(plugins): expose effective context budget in hooks
Add optional context budget/source/reference metadata to plugin hook contexts plus llm_output and sanitized model_call_* hook events. Thread the existing resolved context-window info through Pi embedded runs, CLI harness runs, and Codex app-server hook emission so plugins can observe the effective budget after agent/model/config caps. Document the metadata and cover the CLI, Pi, Codex app-server, and model-call paths with focused tests. Fixes #64327.
This commit is contained in:
@@ -13,6 +13,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Codex app-server: stream commentary preambles into editable channel progress drafts without promoting them to final answers.
|
||||
- Codex migration: remove the bundled `codex-cli` backend and repair legacy `codex-cli/*` model refs to the Codex app-server route on `openai/*`.
|
||||
- Gateway/startup: add owner-level startup trace attribution for auth, plugin loading, lookup counts, and plugin sidecar services. (#81738) Thanks @samzong.
|
||||
- Plugins/hooks: expose the resolved effective `contextTokenBudget` plus source/reference metadata on `llm_output` and sanitized `model_call_*` hook events/contexts so plugin cost and context-health alerts can use agent-level context caps. Fixes #64327. Thanks @BunsDev.
|
||||
- Channels/status reactions: wire `StatusReactionController` into WhatsApp message turns (queued → thinking → tool → done/error lifecycle, on par with Telegram and Discord), add `deploy`/`build`/`concierge` emoji categories with tool-token routing, and replace the status reaction defaults with self-explanatory emoji (🧠 thinking, 🛠️ tool, 💻 coding, 🌐 web, ⏳ stallSoft, ⚠️ stallHard, ✅ done, ❌ error, 🗜️ compacting) so stall and lifecycle reactions read as status indicators instead of emotional commentary. Fixes #59077. (#80612) Thanks @gado-ships-it.
|
||||
- Control UI: add a browser-local Text size setting in Appearance and Quick Settings, scaling chat and dense UI text while keeping inputs above the mobile Safari focus-zoom threshold. Fixes #8547. Thanks @BunsDev.
|
||||
- Docs: add a dedicated ds4 provider page with local DeepSeek V4 Flash config, on-demand startup, context sizing, and live verification steps.
|
||||
|
||||
@@ -114,7 +114,7 @@ observation-only.
|
||||
|
||||
- `model_call_started` / `model_call_ended` - observe sanitized provider/model call metadata, timing, outcome, and bounded request-id hashes without prompt or response content
|
||||
- `llm_input` - observe provider input (system prompt, prompt, history)
|
||||
- `llm_output` - observe provider output
|
||||
- `llm_output` - observe provider output, usage, and the resolved `contextTokenBudget` when available
|
||||
|
||||
**Tools**
|
||||
|
||||
@@ -287,7 +287,11 @@ that should not receive raw prompts, history, responses, headers, request
|
||||
bodies, or provider request IDs. These hooks include stable metadata such as
|
||||
`runId`, `callId`, `provider`, `model`, optional `api`/`transport`, terminal
|
||||
`durationMs`/`outcome`, and `upstreamRequestIdHash` when OpenClaw can derive a
|
||||
bounded provider request-id hash.
|
||||
bounded provider request-id hash. When the runtime has resolved context-window
|
||||
metadata, the hook event and context also include `contextTokenBudget`, the
|
||||
effective token budget after model/config/agent caps, plus
|
||||
`contextWindowSource` and `contextWindowReferenceTokens` when a lower cap was
|
||||
applied.
|
||||
|
||||
`before_agent_finalize` runs only when a harness is about to accept a natural
|
||||
final assistant answer. It is not the `/stop` cancellation path and does not
|
||||
|
||||
@@ -62,6 +62,12 @@ function createParams(sessionFile: string, workspaceDir: string): EmbeddedRunAtt
|
||||
provider: "codex",
|
||||
modelId: "gpt-5.4-codex",
|
||||
model: createCodexTestModel("codex"),
|
||||
contextTokenBudget: 150_000,
|
||||
contextWindowInfo: {
|
||||
tokens: 150_000,
|
||||
referenceTokens: 200_000,
|
||||
source: "agentContextTokens",
|
||||
},
|
||||
thinkLevel: "medium",
|
||||
disableTools: true,
|
||||
timeoutMs: 5_000,
|
||||
@@ -2647,19 +2653,34 @@ describe("runCodexAppServerAttempt", () => {
|
||||
resolvedRef?: string;
|
||||
runId?: string;
|
||||
sessionId?: string;
|
||||
contextTokenBudget?: number;
|
||||
contextWindowSource?: string;
|
||||
contextWindowReferenceTokens?: number;
|
||||
},
|
||||
{
|
||||
runId?: string;
|
||||
sessionId?: string;
|
||||
contextTokenBudget?: number;
|
||||
contextWindowSource?: string;
|
||||
contextWindowReferenceTokens?: number;
|
||||
},
|
||||
{ runId?: string; sessionId?: string },
|
||||
];
|
||||
expect(llmOutputPayload.runId).toBe("run-1");
|
||||
expect(llmOutputPayload.sessionId).toBe("session-1");
|
||||
expect(llmOutputPayload.provider).toBe("codex");
|
||||
expect(llmOutputPayload.model).toBe("gpt-5.4-codex");
|
||||
expect(llmOutputPayload.contextTokenBudget).toBe(150_000);
|
||||
expect(llmOutputPayload.contextWindowSource).toBe("agentContextTokens");
|
||||
expect(llmOutputPayload.contextWindowReferenceTokens).toBe(200_000);
|
||||
expect(llmOutputPayload.resolvedRef).toBe("codex/gpt-5.4-codex");
|
||||
expect(llmOutputPayload.harnessId).toBe("codex");
|
||||
expect(llmOutputPayload.assistantTexts).toEqual(["hello back"]);
|
||||
expect(llmOutputPayload.lastAssistant?.role).toBe("assistant");
|
||||
expect(llmOutputContext.runId).toBe("run-1");
|
||||
expect(llmOutputContext.sessionId).toBe("session-1");
|
||||
expect(llmOutputContext.contextTokenBudget).toBe(150_000);
|
||||
expect(llmOutputContext.contextWindowSource).toBe("agentContextTokens");
|
||||
expect(llmOutputContext.contextWindowReferenceTokens).toBe(200_000);
|
||||
const [agentEndPayload, agentEndContext] = mockCall(agentEnd, "agent_end") as [
|
||||
{ messages?: Array<{ role?: string }>; success?: boolean },
|
||||
{ runId?: string; sessionId?: string },
|
||||
|
||||
@@ -561,6 +561,19 @@ export async function runCodexAppServerAttempt(
|
||||
});
|
||||
const hadSessionFile = await pathExists(params.sessionFile);
|
||||
let historyMessages = (await readMirroredSessionHistoryMessages(params.sessionFile)) ?? [];
|
||||
const hookContextWindowFields = {
|
||||
...(params.contextWindowInfo?.tokens
|
||||
? { contextTokenBudget: params.contextWindowInfo.tokens }
|
||||
: params.contextTokenBudget
|
||||
? { contextTokenBudget: params.contextTokenBudget }
|
||||
: {}),
|
||||
...(params.contextWindowInfo?.source
|
||||
? { contextWindowSource: params.contextWindowInfo.source }
|
||||
: {}),
|
||||
...(params.contextWindowInfo?.referenceTokens
|
||||
? { contextWindowReferenceTokens: params.contextWindowInfo.referenceTokens }
|
||||
: {}),
|
||||
};
|
||||
const hookContext = {
|
||||
runId: params.runId,
|
||||
agentId: sessionAgentId,
|
||||
@@ -570,6 +583,7 @@ export async function runCodexAppServerAttempt(
|
||||
messageProvider: params.messageProvider ?? undefined,
|
||||
trigger: params.trigger,
|
||||
channelId: params.messageChannel ?? params.messageProvider ?? undefined,
|
||||
...hookContextWindowFields,
|
||||
};
|
||||
if (activeContextEngine) {
|
||||
await bootstrapHarnessContextEngine({
|
||||
@@ -1557,6 +1571,7 @@ export async function runCodexAppServerAttempt(
|
||||
sessionId: params.sessionId,
|
||||
provider: params.provider,
|
||||
model: params.modelId,
|
||||
...hookContextWindowFields,
|
||||
resolvedRef:
|
||||
params.runtimePlan?.observability.resolvedRef ?? `${params.provider}/${params.modelId}`,
|
||||
...(params.runtimePlan?.observability.harnessId
|
||||
@@ -1798,6 +1813,7 @@ export async function runCodexAppServerAttempt(
|
||||
sessionId: params.sessionId,
|
||||
provider: params.provider,
|
||||
model: params.modelId,
|
||||
...hookContextWindowFields,
|
||||
resolvedRef:
|
||||
params.runtimePlan?.observability.resolvedRef ?? `${params.provider}/${params.modelId}`,
|
||||
...(params.runtimePlan?.observability.harnessId
|
||||
|
||||
@@ -147,6 +147,11 @@ function buildPreparedContext(params?: {
|
||||
reusableCliSession: params?.cliSessionId ? { sessionId: params.cliSessionId } : {},
|
||||
modelId: "gpt-5.4",
|
||||
normalizedModel: "gpt-5.4",
|
||||
contextWindowInfo: {
|
||||
tokens: 150_000,
|
||||
referenceTokens: 200_000,
|
||||
source: "agentContextTokens",
|
||||
},
|
||||
systemPrompt: "You are a helpful assistant.",
|
||||
systemPromptReport: {} as PreparedCliRunContext["systemPromptReport"],
|
||||
bootstrapPromptWarningLines: [],
|
||||
@@ -665,13 +670,22 @@ describe("runCliAgent reliability", () => {
|
||||
expect(llmOutputEvent.sessionId).toBe("s1");
|
||||
expect(llmOutputEvent.provider).toBe("codex-cli");
|
||||
expect(llmOutputEvent.model).toBe("gpt-5.4");
|
||||
expect(llmOutputEvent.contextTokenBudget).toBe(150_000);
|
||||
expect(llmOutputEvent.contextWindowSource).toBe("agentContextTokens");
|
||||
expect(llmOutputEvent.contextWindowReferenceTokens).toBe(200_000);
|
||||
expect(llmOutputEvent.assistantTexts).toEqual(["hello from cli"]);
|
||||
const lastAssistant = requireRecord(llmOutputEvent.lastAssistant, "last assistant");
|
||||
expect(lastAssistant.role).toBe("assistant");
|
||||
expect(lastAssistant.content).toEqual([{ type: "text", text: "hello from cli" }]);
|
||||
expect(lastAssistant.provider).toBe("codex-cli");
|
||||
expect(lastAssistant.model).toBe("gpt-5.4");
|
||||
expect(callArg(hookRunner.runLlmOutput, 0, 1, "llm_output context")).toBeTypeOf("object");
|
||||
const llmOutputContext = requireRecord(
|
||||
callArg(hookRunner.runLlmOutput, 0, 1, "llm_output context"),
|
||||
"llm_output context",
|
||||
);
|
||||
expect(llmOutputContext.contextTokenBudget).toBe(150_000);
|
||||
expect(llmOutputContext.contextWindowSource).toBe("agentContextTokens");
|
||||
expect(llmOutputContext.contextWindowReferenceTokens).toBe(200_000);
|
||||
|
||||
const agentEndEvent = requireRecord(
|
||||
callArg(hookRunner.runAgentEnd, 0, 0, "agent_end event"),
|
||||
|
||||
@@ -166,6 +166,15 @@ export async function runPreparedCliAgent(
|
||||
sessionId: params.sessionId,
|
||||
workspaceDir: params.workspaceDir,
|
||||
trigger: params.trigger,
|
||||
...(context.contextWindowInfo?.tokens
|
||||
? { contextTokenBudget: context.contextWindowInfo.tokens }
|
||||
: {}),
|
||||
...(context.contextWindowInfo?.source
|
||||
? { contextWindowSource: context.contextWindowInfo.source }
|
||||
: {}),
|
||||
...(context.contextWindowInfo?.referenceTokens
|
||||
? { contextWindowReferenceTokens: context.contextWindowInfo.referenceTokens }
|
||||
: {}),
|
||||
...buildAgentHookContextChannelFields(params),
|
||||
} as const;
|
||||
|
||||
@@ -308,6 +317,15 @@ export async function runPreparedCliAgent(
|
||||
sessionId: params.sessionId,
|
||||
provider: params.provider,
|
||||
model: context.modelId,
|
||||
...(context.contextWindowInfo?.tokens
|
||||
? { contextTokenBudget: context.contextWindowInfo.tokens }
|
||||
: {}),
|
||||
...(context.contextWindowInfo?.source
|
||||
? { contextWindowSource: context.contextWindowInfo.source }
|
||||
: {}),
|
||||
...(context.contextWindowInfo?.referenceTokens
|
||||
? { contextWindowReferenceTokens: context.contextWindowInfo.referenceTokens }
|
||||
: {}),
|
||||
resolvedRef: `${params.provider}/${context.modelId}`,
|
||||
assistantTexts,
|
||||
...(lastAssistant ? { lastAssistant } : {}),
|
||||
|
||||
@@ -30,6 +30,8 @@ import { CLI_AUTH_EPOCH_VERSION, resolveCliAuthEpoch } from "../cli-auth-epoch.j
|
||||
import { resolveCliBackendConfig } from "../cli-backends.js";
|
||||
import { hashCliSessionText, resolveCliSessionReuse } from "../cli-session.js";
|
||||
import { claudeCliSessionTranscriptHasContent } from "../command/attempt-execution.helpers.js";
|
||||
import { resolveContextWindowInfo } from "../context-window-guard.js";
|
||||
import { DEFAULT_CONTEXT_TOKENS } from "../defaults.js";
|
||||
import { resolveHeartbeatPromptForSystemPrompt } from "../heartbeat-system-prompt.js";
|
||||
import {
|
||||
resolveBootstrapMaxChars,
|
||||
@@ -156,6 +158,12 @@ export async function prepareCliRunContext(
|
||||
const modelId = (params.model ?? "default").trim() || "default";
|
||||
const normalizedModel = normalizeCliModel(modelId, backendResolved.config);
|
||||
const modelDisplay = `${params.provider}/${modelId}`;
|
||||
const contextWindowInfo = resolveContextWindowInfo({
|
||||
cfg: params.config,
|
||||
provider: params.provider,
|
||||
modelId,
|
||||
defaultTokens: DEFAULT_CONTEXT_TOKENS,
|
||||
});
|
||||
|
||||
const sessionLabel = params.sessionKey ?? params.sessionId;
|
||||
const { bootstrapFiles, contextFiles } = await prepareDeps.resolveBootstrapContextForRun({
|
||||
@@ -471,6 +479,7 @@ export async function prepareCliRunContext(
|
||||
reusableCliSession,
|
||||
modelId,
|
||||
normalizedModel,
|
||||
contextWindowInfo,
|
||||
systemPrompt,
|
||||
systemPromptReport,
|
||||
bootstrapPromptWarningLines: bootstrapPromptWarning.lines,
|
||||
|
||||
@@ -10,6 +10,7 @@ import type { PromptImageOrderEntry } from "../../media/prompt-image-order.js";
|
||||
import type { InputProvenance } from "../../sessions/input-provenance.js";
|
||||
import type { BootstrapContextMode } from "../bootstrap-files.js";
|
||||
import type { ResolvedCliBackend } from "../cli-backends.js";
|
||||
import type { ContextWindowInfo } from "../context-window-guard.js";
|
||||
import type {
|
||||
CurrentTurnPromptContext,
|
||||
EmbeddedRunTrigger,
|
||||
@@ -114,6 +115,7 @@ export type PreparedCliRunContext = {
|
||||
reusableCliSession: CliReusableSession;
|
||||
modelId: string;
|
||||
normalizedModel: string;
|
||||
contextWindowInfo?: ContextWindowInfo;
|
||||
systemPrompt: string;
|
||||
systemPromptReport: SessionSystemPromptReport;
|
||||
bootstrapPromptWarningLines: string[];
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import type { PluginHookAgentContext } from "../../plugins/hook-types.js";
|
||||
import type {
|
||||
PluginHookAgentContext,
|
||||
PluginHookContextWindowSource,
|
||||
} from "../../plugins/hook-types.js";
|
||||
|
||||
export type AgentHarnessHookContext = {
|
||||
runId: string;
|
||||
@@ -12,6 +15,9 @@ export type AgentHarnessHookContext = {
|
||||
messageProvider?: string;
|
||||
trigger?: string;
|
||||
channelId?: string;
|
||||
contextTokenBudget?: number;
|
||||
contextWindowSource?: PluginHookContextWindowSource;
|
||||
contextWindowReferenceTokens?: number;
|
||||
};
|
||||
|
||||
export function buildAgentHookContext(params: AgentHarnessHookContext): PluginHookAgentContext {
|
||||
@@ -27,5 +33,10 @@ export function buildAgentHookContext(params: AgentHarnessHookContext): PluginHo
|
||||
...(params.messageProvider ? { messageProvider: params.messageProvider } : {}),
|
||||
...(params.trigger ? { trigger: params.trigger } : {}),
|
||||
...(params.channelId ? { channelId: params.channelId } : {}),
|
||||
...(params.contextTokenBudget ? { contextTokenBudget: params.contextTokenBudget } : {}),
|
||||
...(params.contextWindowSource ? { contextWindowSource: params.contextWindowSource } : {}),
|
||||
...(params.contextWindowReferenceTokens
|
||||
? { contextWindowReferenceTokens: params.contextWindowReferenceTokens }
|
||||
: {}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1288,6 +1288,7 @@ export async function runEmbeddedPiAgent(
|
||||
allowGatewaySubagentBinding: params.allowGatewaySubagentBinding,
|
||||
contextEngine,
|
||||
contextTokenBudget: ctxInfo.tokens,
|
||||
contextWindowInfo: ctxInfo,
|
||||
skillsSnapshot: params.skillsSnapshot,
|
||||
prompt,
|
||||
transcriptPrompt: params.transcriptPrompt,
|
||||
|
||||
@@ -390,6 +390,9 @@ describe("wrapStreamFnWithDiagnosticModelCallEvents", () => {
|
||||
model: "gpt-5.4",
|
||||
api: "openai-responses",
|
||||
transport: "http",
|
||||
contextTokenBudget: 150_000,
|
||||
contextWindowSource: "agentContextTokens",
|
||||
contextWindowReferenceTokens: 200_000,
|
||||
trace: createDiagnosticTraceContext(),
|
||||
nextCallId: () => "call-hook",
|
||||
},
|
||||
@@ -413,16 +416,25 @@ describe("wrapStreamFnWithDiagnosticModelCallEvents", () => {
|
||||
expect(startedEvent.model).toBe("gpt-5.4");
|
||||
expect(startedEvent.api).toBe("openai-responses");
|
||||
expect(startedEvent.transport).toBe("http");
|
||||
expect(startedEvent.contextTokenBudget).toBe(150_000);
|
||||
expect(startedEvent.contextWindowSource).toBe("agentContextTokens");
|
||||
expect(startedEvent.contextWindowReferenceTokens).toBe(200_000);
|
||||
const startedCtx = requireMockRecordArg(started, 0, 1, "started hook context");
|
||||
expect(startedCtx.runId).toBe("run-1");
|
||||
expect(startedCtx.sessionKey).toBe("session-key");
|
||||
expect(startedCtx.sessionId).toBe("session-id");
|
||||
expect(startedCtx.modelProviderId).toBe("openai");
|
||||
expect(startedCtx.modelId).toBe("gpt-5.4");
|
||||
expect(startedCtx.contextTokenBudget).toBe(150_000);
|
||||
expect(startedCtx.contextWindowSource).toBe("agentContextTokens");
|
||||
expect(startedCtx.contextWindowReferenceTokens).toBe(200_000);
|
||||
const endedEvent = requireMockRecordArg(ended, 0, 0, "ended hook event");
|
||||
expect(endedEvent.runId).toBe("run-1");
|
||||
expect(endedEvent.callId).toBe("call-hook");
|
||||
expect(endedEvent.outcome).toBe("completed");
|
||||
expect(endedEvent.contextTokenBudget).toBe(150_000);
|
||||
expect(endedEvent.contextWindowSource).toBe("agentContextTokens");
|
||||
expect(endedEvent.contextWindowReferenceTokens).toBe(200_000);
|
||||
expectNumberField(endedEvent, "durationMs");
|
||||
expectNumberField(endedEvent, "responseStreamBytes");
|
||||
expectNumberField(endedEvent, "timeToFirstByteMs");
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
import { getGlobalHookRunner } from "../../../plugins/hook-runner-global.js";
|
||||
import type {
|
||||
PluginHookAgentContext,
|
||||
PluginHookContextWindowSource,
|
||||
PluginHookModelCallEndedEvent,
|
||||
PluginHookModelCallStartedEvent,
|
||||
} from "../../../plugins/hook-types.js";
|
||||
@@ -33,6 +34,9 @@ type ModelCallDiagnosticContext = {
|
||||
model: string;
|
||||
api?: string;
|
||||
transport?: string;
|
||||
contextTokenBudget?: number;
|
||||
contextWindowSource?: PluginHookContextWindowSource;
|
||||
contextWindowReferenceTokens?: number;
|
||||
trace: DiagnosticTraceContext;
|
||||
nextCallId: () => string;
|
||||
onStarted?: () => void;
|
||||
@@ -150,6 +154,11 @@ function baseModelCallEvent(
|
||||
model: ctx.model,
|
||||
...(ctx.api && { api: ctx.api }),
|
||||
...(ctx.transport && { transport: ctx.transport }),
|
||||
...(ctx.contextTokenBudget ? { contextTokenBudget: ctx.contextTokenBudget } : {}),
|
||||
...(ctx.contextWindowSource ? { contextWindowSource: ctx.contextWindowSource } : {}),
|
||||
...(ctx.contextWindowReferenceTokens
|
||||
? { contextWindowReferenceTokens: ctx.contextWindowReferenceTokens }
|
||||
: {}),
|
||||
trace,
|
||||
};
|
||||
}
|
||||
@@ -189,6 +198,13 @@ function modelCallHookEventBase(eventBase: ModelCallEventBase): PluginHookModelC
|
||||
model: eventBase.model,
|
||||
...(eventBase.api ? { api: eventBase.api } : {}),
|
||||
...(eventBase.transport ? { transport: eventBase.transport } : {}),
|
||||
...(eventBase.contextTokenBudget ? { contextTokenBudget: eventBase.contextTokenBudget } : {}),
|
||||
...(eventBase.contextWindowSource
|
||||
? { contextWindowSource: eventBase.contextWindowSource }
|
||||
: {}),
|
||||
...(eventBase.contextWindowReferenceTokens
|
||||
? { contextWindowReferenceTokens: eventBase.contextWindowReferenceTokens }
|
||||
: {}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -200,6 +216,13 @@ function modelCallHookContext(eventBase: ModelCallEventBase): PluginHookAgentCon
|
||||
...(eventBase.sessionId ? { sessionId: eventBase.sessionId } : {}),
|
||||
modelProviderId: eventBase.provider,
|
||||
modelId: eventBase.model,
|
||||
...(eventBase.contextTokenBudget ? { contextTokenBudget: eventBase.contextTokenBudget } : {}),
|
||||
...(eventBase.contextWindowSource
|
||||
? { contextWindowSource: eventBase.contextWindowSource }
|
||||
: {}),
|
||||
...(eventBase.contextWindowReferenceTokens
|
||||
? { contextWindowReferenceTokens: eventBase.contextWindowReferenceTokens }
|
||||
: {}),
|
||||
}) as PluginHookAgentContext;
|
||||
}
|
||||
|
||||
|
||||
@@ -2447,6 +2447,15 @@ export async function runEmbeddedAttempt(
|
||||
model: params.modelId,
|
||||
api: params.model.api,
|
||||
transport: effectiveAgentTransport,
|
||||
...(params.contextWindowInfo?.tokens
|
||||
? { contextTokenBudget: params.contextWindowInfo.tokens }
|
||||
: {}),
|
||||
...(params.contextWindowInfo?.source
|
||||
? { contextWindowSource: params.contextWindowInfo.source }
|
||||
: {}),
|
||||
...(params.contextWindowInfo?.referenceTokens
|
||||
? { contextWindowReferenceTokens: params.contextWindowInfo.referenceTokens }
|
||||
: {}),
|
||||
trace: runTrace,
|
||||
nextCallId: () => `${params.runId}:model:${(diagnosticModelCallSeq += 1)}`,
|
||||
onStarted: () => {
|
||||
@@ -4006,6 +4015,15 @@ export async function runEmbeddedAttempt(
|
||||
sessionId: params.sessionId,
|
||||
provider: params.provider,
|
||||
model: params.modelId,
|
||||
...(params.contextWindowInfo?.tokens
|
||||
? { contextTokenBudget: params.contextWindowInfo.tokens }
|
||||
: {}),
|
||||
...(params.contextWindowInfo?.source
|
||||
? { contextWindowSource: params.contextWindowInfo.source }
|
||||
: {}),
|
||||
...(params.contextWindowInfo?.referenceTokens
|
||||
? { contextWindowReferenceTokens: params.contextWindowInfo.referenceTokens }
|
||||
: {}),
|
||||
resolvedRef:
|
||||
params.runtimePlan?.observability.resolvedRef ??
|
||||
`${params.provider}/${params.modelId}`,
|
||||
@@ -4024,6 +4042,15 @@ export async function runEmbeddedAttempt(
|
||||
sessionId: params.sessionId,
|
||||
workspaceDir: params.workspaceDir,
|
||||
trigger: params.trigger,
|
||||
...(params.contextWindowInfo?.tokens
|
||||
? { contextTokenBudget: params.contextWindowInfo.tokens }
|
||||
: {}),
|
||||
...(params.contextWindowInfo?.source
|
||||
? { contextWindowSource: params.contextWindowInfo.source }
|
||||
: {}),
|
||||
...(params.contextWindowInfo?.referenceTokens
|
||||
? { contextWindowReferenceTokens: params.contextWindowInfo.referenceTokens }
|
||||
: {}),
|
||||
...buildAgentHookContextChannelFields(params),
|
||||
},
|
||||
)
|
||||
|
||||
@@ -26,12 +26,20 @@ type EmbeddedRunAttemptBase = Omit<
|
||||
"provider" | "model" | "authProfileId" | "authProfileIdSource" | "thinkLevel" | "lane" | "enqueue"
|
||||
>;
|
||||
|
||||
export type EmbeddedRunContextWindowInfo = {
|
||||
tokens: number;
|
||||
referenceTokens?: number;
|
||||
source: "model" | "modelsConfig" | "agentContextTokens" | "default";
|
||||
};
|
||||
|
||||
export type EmbeddedRunAttemptParams = EmbeddedRunAttemptBase & {
|
||||
initialReplayState?: EmbeddedRunReplayState;
|
||||
/** Pluggable context engine for ingest/assemble/compact lifecycle. */
|
||||
contextEngine?: ContextEngine;
|
||||
/** Resolved model context window in tokens for assemble/compact budgeting. */
|
||||
contextTokenBudget?: number;
|
||||
/** Source metadata for the resolved model context budget. */
|
||||
contextWindowInfo?: EmbeddedRunContextWindowInfo;
|
||||
/** Resolved API key for this run when runtime auth did not replace it. */
|
||||
resolvedApiKey?: string;
|
||||
/** Auth profile resolved for this attempt's provider/model call. */
|
||||
|
||||
@@ -458,6 +458,9 @@ type DiagnosticModelCallBaseEvent = DiagnosticBaseEvent & {
|
||||
model: string;
|
||||
api?: string;
|
||||
transport?: string;
|
||||
contextTokenBudget?: number;
|
||||
contextWindowSource?: "model" | "modelsConfig" | "agentContextTokens" | "default";
|
||||
contextWindowReferenceTokens?: number;
|
||||
upstreamRequestIdHash?: string;
|
||||
};
|
||||
|
||||
|
||||
@@ -196,8 +196,20 @@ export type PluginHookAgentContext = {
|
||||
messageProvider?: string;
|
||||
trigger?: string;
|
||||
channelId?: string;
|
||||
/** Resolved effective context-token budget after model/config/agent caps. */
|
||||
contextTokenBudget?: number;
|
||||
/** Source that supplied the resolved context-token budget. */
|
||||
contextWindowSource?: PluginHookContextWindowSource;
|
||||
/** Native/configured reference window when a lower cap wins. */
|
||||
contextWindowReferenceTokens?: number;
|
||||
};
|
||||
|
||||
export type PluginHookContextWindowSource =
|
||||
| "model"
|
||||
| "modelsConfig"
|
||||
| "agentContextTokens"
|
||||
| "default";
|
||||
|
||||
export type PluginHookBeforeAgentReplyEvent = {
|
||||
cleanedBody: string;
|
||||
};
|
||||
@@ -229,6 +241,12 @@ export type PluginHookModelCallBaseEvent = {
|
||||
model: string;
|
||||
api?: string;
|
||||
transport?: string;
|
||||
/** Resolved effective context-token budget after model/config/agent caps. */
|
||||
contextTokenBudget?: number;
|
||||
/** Source that supplied the resolved context-token budget. */
|
||||
contextWindowSource?: PluginHookContextWindowSource;
|
||||
/** Native/configured reference window when a lower cap wins. */
|
||||
contextWindowReferenceTokens?: number;
|
||||
};
|
||||
|
||||
export type PluginHookModelCallStartedEvent = PluginHookModelCallBaseEvent;
|
||||
@@ -249,6 +267,12 @@ export type PluginHookLlmOutputEvent = {
|
||||
sessionId: string;
|
||||
provider: string;
|
||||
model: string;
|
||||
/** Resolved effective context-token budget after model/config/agent caps. */
|
||||
contextTokenBudget?: number;
|
||||
/** Source that supplied the resolved context-token budget. */
|
||||
contextWindowSource?: PluginHookContextWindowSource;
|
||||
/** Native/configured reference window when a lower cap wins. */
|
||||
contextWindowReferenceTokens?: number;
|
||||
/**
|
||||
* Fully resolved provider/model ref used for the call.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user