@cognipeer/agent-sdk 0.10.1 → 0.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -4601,18 +4601,60 @@ function createToolsNode(initialTools, opts) {
4601
4601
  toolStateRef.__currentToolCallId = toolCallId;
4602
4602
  delete toolStateRef.__awaitingApproval;
4603
4603
  }
4604
- let hookAskedForApproval = false;
4605
- let hookApprovalPrompt;
4606
- let hookResult;
4607
- if (host?.has("preToolUse")) {
4604
+ const stableJson = (value) => {
4605
+ try {
4606
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
4607
+ if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
4608
+ const keys = Object.keys(value).sort();
4609
+ return `{${keys.map((k) => JSON.stringify(k) + ":" + stableJson(value[k])).join(",")}}`;
4610
+ } catch {
4611
+ return String(value);
4612
+ }
4613
+ };
4614
+ const rejectByPolicy = (gateReason, gatedArgs) => {
4615
+ const reason = gateReason || `Tool call blocked by policy: ${toolName}`;
4616
+ push({ role: "tool", content: reason, tool_call_id: toolCallId, name: tc.name });
4617
+ toolHistory.push({
4618
+ executionId: nanoid.nanoid(),
4619
+ toolName,
4620
+ args: gatedArgs,
4621
+ output: reason,
4622
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4623
+ tool_call_id: toolCallId,
4624
+ status: "rejected"
4625
+ });
4626
+ onEvent?.({ type: "tool_call", phase: "skipped", name: toolName, id: tc.id, args: gatedArgs, error: { message: reason } });
4627
+ recordTraceEvent(traceSession, {
4628
+ type: "tool_call",
4629
+ label: `Tool Denied - ${toolName}`,
4630
+ actor: { scope: "tool", name: toolName, role: "tool" },
4631
+ status: "skipped",
4632
+ toolDetails,
4633
+ toolExecutionId: toolCallId,
4634
+ sections: buildToolTraceSections({
4635
+ args: gatedArgs,
4636
+ executionId: toolCallId,
4637
+ output: reason,
4638
+ status: "skipped",
4639
+ summary: reason,
4640
+ toolDetails,
4641
+ toolName
4642
+ })
4643
+ });
4644
+ toolCount += 1;
4645
+ return { status: "error" };
4646
+ };
4647
+ const applyPreToolUseGate = async (candidate) => {
4648
+ if (!host?.has("preToolUse")) return { ok: true, args: candidate, ask: false };
4608
4649
  const gate = await host.runGate("preToolUse", {
4609
4650
  toolName,
4610
4651
  toolCallId,
4611
- args,
4652
+ args: candidate,
4612
4653
  tool,
4613
4654
  executionCount: countSuccessfulToolExecutions(toolHistory, toolName)
4614
4655
  });
4615
- if (gate.input.args !== args) {
4656
+ let next = candidate;
4657
+ if (gate.input.args !== candidate) {
4616
4658
  const revalidated = validateToolArgs(tool, gate.input.args);
4617
4659
  if (!revalidated.ok && resolved.toolResponses.schemaValidation === "strict") {
4618
4660
  const message = `Tool argument validation failed after a plugin rewrite for ${toolName}: ${revalidated.message}`;
@@ -4620,48 +4662,31 @@ function createToolsNode(initialTools, opts) {
4620
4662
  markToolFailure(message, toolName, toolCallId);
4621
4663
  onEvent?.({ type: "tool_call", phase: "error", name: toolName, id: tc.id, args: gate.input.args, error: { message } });
4622
4664
  toolCount += 1;
4623
- return { status: "error" };
4665
+ return { ok: false, outcome: { status: "error" } };
4624
4666
  }
4625
- args = revalidated.ok ? revalidated.value : gate.input.args;
4667
+ next = revalidated.ok ? revalidated.value : gate.input.args;
4626
4668
  }
4627
4669
  if (gate.decision === "deny") {
4628
- const reason = gate.reason || `Tool call blocked by policy: ${toolName}`;
4629
- push({ role: "tool", content: reason, tool_call_id: toolCallId, name: tc.name });
4630
- toolHistory.push({
4631
- executionId: nanoid.nanoid(),
4632
- toolName,
4633
- args,
4634
- output: reason,
4635
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4636
- tool_call_id: toolCallId,
4637
- status: "rejected"
4638
- });
4639
- onEvent?.({ type: "tool_call", phase: "skipped", name: toolName, id: tc.id, args, error: { message: reason } });
4640
- recordTraceEvent(traceSession, {
4641
- type: "tool_call",
4642
- label: `Tool Denied - ${toolName}`,
4643
- actor: { scope: "tool", name: toolName, role: "tool" },
4644
- status: "skipped",
4645
- toolDetails,
4646
- toolExecutionId: toolCallId,
4647
- sections: buildToolTraceSections({
4648
- args,
4649
- executionId: toolCallId,
4650
- output: reason,
4651
- status: "skipped",
4652
- summary: reason,
4653
- toolDetails,
4654
- toolName
4655
- })
4656
- });
4657
- toolCount += 1;
4658
- return { status: "error" };
4659
- }
4660
- if (gate.decision === "ask") {
4661
- hookAskedForApproval = true;
4662
- hookApprovalPrompt = gate.approvalPrompt;
4670
+ return { ok: false, outcome: rejectByPolicy(gate.reason, next) };
4663
4671
  }
4664
- if (gate.shortCircuit !== void 0) hookResult = gate.shortCircuit;
4672
+ return {
4673
+ ok: true,
4674
+ args: next,
4675
+ ask: gate.decision === "ask",
4676
+ approvalPrompt: gate.approvalPrompt,
4677
+ result: gate.shortCircuit
4678
+ };
4679
+ };
4680
+ let hookAskedForApproval = false;
4681
+ let hookApprovalPrompt;
4682
+ let hookResult;
4683
+ {
4684
+ const gated = await applyPreToolUseGate(args);
4685
+ if (!gated.ok) return gated.outcome;
4686
+ args = gated.args;
4687
+ hookAskedForApproval = gated.ask;
4688
+ hookApprovalPrompt = gated.approvalPrompt;
4689
+ if (gated.result !== void 0) hookResult = gated.result;
4665
4690
  }
4666
4691
  const maxExecutionsPerRun = normalizeMaxExecutionsPerRun(tool.maxExecutionsPerRun);
4667
4692
  if (maxExecutionsPerRun !== null) {
@@ -4746,7 +4771,34 @@ function createToolsNode(initialTools, opts) {
4746
4771
  return { status: "rejected", approval: approvalEntry };
4747
4772
  }
4748
4773
  if (approvalEntry.status === "approved" && approvalEntry.approvedArgs !== void 0) {
4749
- args = approvalEntry.approvedArgs;
4774
+ const approvedArgs = approvalEntry.approvedArgs;
4775
+ if (stableJson(approvedArgs) !== stableJson(args)) {
4776
+ const settleRefused = (resolution) => {
4777
+ approvalEntry.metadata = { ...approvalEntry.metadata || {}, resolution };
4778
+ approvalEntry.status = "executed";
4779
+ approvalEntry.resolvedAt = (/* @__PURE__ */ new Date()).toISOString();
4780
+ pendingByCallId.set(toolCallId, approvalEntry);
4781
+ };
4782
+ const revalidated = validateToolArgs(tool, approvedArgs);
4783
+ if (!revalidated.ok && resolved.toolResponses.schemaValidation === "strict") {
4784
+ const message = `Tool argument validation failed for reviewer-edited arguments of ${toolName}: ${revalidated.message}`;
4785
+ push({ role: "tool", content: message, tool_call_id: toolCallId, name: tc.name });
4786
+ markToolFailure(message, toolName, toolCallId);
4787
+ settleRefused("invalid_args");
4788
+ onEvent?.({ type: "tool_call", phase: "error", name: toolName, id: tc.id, args: approvedArgs, error: { message } });
4789
+ toolCount += 1;
4790
+ return { status: "error", approval: approvalEntry };
4791
+ }
4792
+ const gated = await applyPreToolUseGate(revalidated.ok ? revalidated.value : approvedArgs);
4793
+ if (!gated.ok) {
4794
+ settleRefused("policy_denied");
4795
+ return { ...gated.outcome, approval: approvalEntry };
4796
+ }
4797
+ args = gated.args;
4798
+ if (gated.result !== void 0 && hookResult === void 0) hookResult = gated.result;
4799
+ } else {
4800
+ args = approvedArgs;
4801
+ }
4750
4802
  onEvent?.({ type: "tool_approval", status: "approved", id: approvalEntry.id, toolName: approvalEntry.toolName, toolCallId: approvalEntry.toolCallId, decidedBy: approvalEntry.decidedBy, comment: approvalEntry.comment });
4751
4803
  }
4752
4804
  }
@@ -4756,16 +4808,6 @@ function createToolsNode(initialTools, opts) {
4756
4808
  const cacheCfg = tool.cache;
4757
4809
  const cacheEnabled = cacheCfg === true || cacheCfg && typeof cacheCfg === "object";
4758
4810
  const cacheKeyFn = cacheEnabled && typeof cacheCfg === "object" ? cacheCfg.keyFn : void 0;
4759
- const stableJson = (value) => {
4760
- try {
4761
- if (value === null || typeof value !== "object") return JSON.stringify(value);
4762
- if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
4763
- const keys = Object.keys(value).sort();
4764
- return `{${keys.map((k) => JSON.stringify(k) + ":" + stableJson(value[k])).join(",")}}`;
4765
- } catch {
4766
- return String(value);
4767
- }
4768
- };
4769
4811
  const cacheKey = cacheEnabled ? `${toolName}|${cacheKeyFn ? cacheKeyFn(args) : stableJson(args)}` : void 0;
4770
4812
  const toolCache = state.toolCache = state.toolCache || {};
4771
4813
  let cachedHit = void 0;
@@ -4813,6 +4855,22 @@ function createToolsNode(initialTools, opts) {
4813
4855
  try {
4814
4856
  if (cachedHit !== void 0) {
4815
4857
  const executionId2 = nanoid.nanoid();
4858
+ if (host?.has("postToolUse")) {
4859
+ const shortGate = await host.runGate("postToolUse", {
4860
+ toolName,
4861
+ toolCallId,
4862
+ args,
4863
+ output: cachedHit,
4864
+ durationMs: 0,
4865
+ executionId: executionId2,
4866
+ source: shortCircuitSource === "plugin" ? "hook" : "cache"
4867
+ });
4868
+ if (shortGate.decision === "deny") {
4869
+ cachedHit = shortGate.reason || `Tool output withheld by policy: ${toolName}`;
4870
+ } else if (shortGate.input.output !== cachedHit) {
4871
+ cachedHit = shortGate.input.output;
4872
+ }
4873
+ }
4816
4874
  const { output: compressedCachedHit, stats: cachedContextPilotStats } = maybeCompressToolOutput(toolName, cachedHit, executionId2);
4817
4875
  const responsePolicy2 = applyToolResponseHardCap(toolName, compressedCachedHit, executionId2, resolved);
4818
4876
  toolHistory.push({
@@ -4979,7 +5037,8 @@ function createToolsNode(initialTools, opts) {
4979
5037
  args,
4980
5038
  output,
4981
5039
  durationMs,
4982
- executionId
5040
+ executionId,
5041
+ source: "tool"
4983
5042
  });
4984
5043
  if (postGate.decision === "deny") {
4985
5044
  output = postGate.reason || `Tool output withheld by policy: ${toolName}`;
@@ -5074,7 +5133,8 @@ function createToolsNode(initialTools, opts) {
5074
5133
  output: message,
5075
5134
  error: error instanceof Error ? error : new Error(message),
5076
5135
  durationMs,
5077
- executionId
5136
+ executionId,
5137
+ source: "tool"
5078
5138
  });
5079
5139
  if (errorGate.decision === "deny") {
5080
5140
  message = errorGate.reason || `Tool error withheld by policy: ${toolName}`;
@@ -5750,7 +5810,14 @@ var DISALLOWED_CTX_KEYS = /* @__PURE__ */ new Set([
5750
5810
  // Session bookkeeping: a resumed run must open its OWN session, so these
5751
5811
  // must not travel in a snapshot.
5752
5812
  "__pluginRunStarted",
5753
- "__pluginRunEnded"
5813
+ "__pluginRunEnded",
5814
+ // Set by restoreSnapshot on the way OUT, consumed by the next invoke. Never
5815
+ // persisted: a snapshot of a restored run would otherwise mark every later
5816
+ // restore as "resumed" forever.
5817
+ "__restoredFromSnapshot",
5818
+ // A per-turn verdict. It describes the run that produced the snapshot, not
5819
+ // the run that will resume from it.
5820
+ "__guardrailBlocked"
5754
5821
  ]);
5755
5822
  var clone = (value) => {
5756
5823
  if (typeof globalThis.structuredClone === "function") {
@@ -6374,6 +6441,7 @@ var SLOT_NAMES = [
6374
6441
  "contextBuilder"
6375
6442
  ];
6376
6443
  var DECISION_RANK = { allow: 0, ask: 1, deny: 2 };
6444
+ var isHookDecision = (value) => typeof value === "string" && Object.prototype.hasOwnProperty.call(DECISION_RANK, value);
6377
6445
  function escalate(current, next) {
6378
6446
  return DECISION_RANK[next] > DECISION_RANK[current] ? next : current;
6379
6447
  }
@@ -6383,6 +6451,15 @@ var HookTimeoutError = class extends Error {
6383
6451
  this.name = "HookTimeoutError";
6384
6452
  }
6385
6453
  };
6454
+ var InvalidHookDecisionError = class extends Error {
6455
+ constructor(plugin, hook, value) {
6456
+ const shown = typeof value === "string" ? JSON.stringify(value) : String(value);
6457
+ super(
6458
+ `plugin "${plugin}" hook "${hook}" returned an unknown decision ${shown}; expected "allow", "ask" or "deny"`
6459
+ );
6460
+ this.name = "InvalidHookDecisionError";
6461
+ }
6462
+ };
6386
6463
  function withTimeout(value, ms, label) {
6387
6464
  if (!ms || !Number.isFinite(ms) || ms <= 0) return Promise.resolve(value);
6388
6465
  return new Promise((resolve, reject) => {
@@ -6672,10 +6749,18 @@ function createPluginHost(plugins = [], options = {}) {
6672
6749
  `plugin "${entry.plugin}" hook "${hook}"`
6673
6750
  );
6674
6751
  output = raw ?? void 0;
6752
+ if (output && typeof output === "object" && output.decision !== void 0 && !isHookDecision(output.decision)) {
6753
+ throw new InvalidHookDecisionError(entry.plugin, hook, output.decision);
6754
+ }
6675
6755
  } catch (err) {
6676
6756
  const message = err instanceof Error ? err.message : String(err);
6677
6757
  const timedOut = err instanceof HookTimeoutError;
6678
- logger.warn(`plugin "${entry.plugin}" hook "${hook}" failed:`, message);
6758
+ const badDecision = err instanceof InvalidHookDecisionError;
6759
+ if (badDecision) {
6760
+ logger.error(`plugin "${entry.plugin}" hook "${hook}" returned an invalid decision:`, message);
6761
+ } else {
6762
+ logger.warn(`plugin "${entry.plugin}" hook "${hook}" failed:`, message);
6763
+ }
6679
6764
  emitPluginEvent(entry, hook, {
6680
6765
  phase: timedOut ? "timeout" : "error",
6681
6766
  error: { message },
@@ -6687,8 +6772,9 @@ function createPluginHost(plugins = [], options = {}) {
6687
6772
  durationMs: performanceNow() - startedAt
6688
6773
  });
6689
6774
  if (entry.failureMode === "closed") {
6775
+ const cause = timedOut ? "timed out" : badDecision ? "invalid decision" : "hook error";
6690
6776
  result.decision = "deny";
6691
- result.reason = `${entry.plugin}: ${timedOut ? "timed out" : "hook error"} (fail-closed) \u2014 ${message}`;
6777
+ result.reason = `${entry.plugin}: ${cause} (fail-closed) \u2014 ${message}`;
6692
6778
  result.deniedBy = entry.plugin;
6693
6779
  result.input = current;
6694
6780
  return result;
@@ -6746,11 +6832,19 @@ function createPluginHost(plugins = [], options = {}) {
6746
6832
  if (output.metadata && typeof output.metadata === "object") {
6747
6833
  result.metadata = { ...result.metadata ?? {}, ...output.metadata };
6748
6834
  }
6749
- const decision = output.decision;
6835
+ let decision = output.decision;
6836
+ if (decision === "ask" && hook !== "preToolUse") {
6837
+ logger.warn(
6838
+ `plugin "${entry.plugin}" returned "ask" from "${hook}", which cannot pause for approval; treating it as "deny".`
6839
+ );
6840
+ decision = "deny";
6841
+ }
6750
6842
  const escalated = decision ? escalate(result.decision, decision) : result.decision;
6751
6843
  const escalatesHere = escalated !== result.decision;
6752
6844
  result.decision = escalated;
6753
- if (typeof output.reason === "string" && (escalatesHere || !result.reason)) {
6845
+ if (escalatesHere) {
6846
+ result.reason = typeof output.reason === "string" ? `${entry.plugin}: ${output.reason}` : void 0;
6847
+ } else if (typeof output.reason === "string" && !result.reason) {
6754
6848
  result.reason = `${entry.plugin}: ${output.reason}`;
6755
6849
  }
6756
6850
  if (mutatedHere) {
@@ -6844,7 +6938,7 @@ async function openPluginSession(runHost, params) {
6844
6938
  const appended = startGate.collected.systemPromptAppend ?? [];
6845
6939
  const systemPromptAppend = appended.length > 0 ? appended.join("\n\n") : void 0;
6846
6940
  const last = messages[messages.length - 1];
6847
- if (params.resumed || last?.role !== "user" || !runHost.has("userPromptSubmit")) {
6941
+ if (params.pausedMidRun || last?.role !== "user" || !runHost.has("userPromptSubmit")) {
6848
6942
  return { messages, systemPromptAppend };
6849
6943
  }
6850
6944
  const content = last.content;
@@ -7089,6 +7183,11 @@ function createAgent(opts) {
7089
7183
  delete nextCtx.__paused;
7090
7184
  state = { ...state, ctx: Object.keys(nextCtx).length > 0 ? nextCtx : void 0 };
7091
7185
  }
7186
+ if (state.ctx?.__restoredFromSnapshot) {
7187
+ const nextCtx = { ...state.ctx };
7188
+ delete nextCtx.__restoredFromSnapshot;
7189
+ state = { ...state, ctx: Object.keys(nextCtx).length > 0 ? nextCtx : void 0 };
7190
+ }
7092
7191
  let resumeStage = null;
7093
7192
  if (state.ctx?.__resumeStage) {
7094
7193
  const nextCtx = { ...state.ctx };
@@ -7363,7 +7462,7 @@ function createAgent(opts) {
7363
7462
  continue;
7364
7463
  }
7365
7464
  if (toolCalls.length === 0) {
7366
- if (soManager && !state.ctx?.__finalizedDueToStructuredOutput && !state.ctx?.__structuredOutputForceFinalize) {
7465
+ if (soManager && !state.ctx?.__finalizedDueToStructuredOutput && !state.ctx?.__structuredOutputForceFinalize && !state.ctx?.__guardrailBlocked) {
7367
7466
  if (soManager.strategy.kind === "native") {
7368
7467
  const assistantText = extractMessageText2(lastMsg);
7369
7468
  if (assistantText) {
@@ -7532,6 +7631,7 @@ function createAgent(opts) {
7532
7631
  delete ctx.__plugins;
7533
7632
  delete ctx.__applySystemPromptContribution;
7534
7633
  delete ctx.__pluginState;
7634
+ delete ctx.__guardrailBlocked;
7535
7635
  const inheritedSession = config?.__pluginSession;
7536
7636
  ctx.__runId = inheritedSession?.runId ?? traceSession?.sessionId ?? `run_${nanoid.nanoid(10)}`;
7537
7637
  const stateHolder = inheritedSession?.stateHolder ?? { value: void 0 };
@@ -7581,9 +7681,11 @@ function createAgent(opts) {
7581
7681
  usage: input.usage || { perRequest: [], totals: {} }
7582
7682
  };
7583
7683
  stateHolder.value = initial;
7584
- const resumed = Boolean(
7585
- input.ctx?.__restoredFromSnapshot || input.ctx?.__paused || input.ctx?.__resumeStage || input.ctx?.__awaitingApproval || input.ctx?.__awaitingUserQuestion
7684
+ const pausedMidRun = Boolean(
7685
+ input.ctx?.__paused || input.ctx?.__resumeStage || input.ctx?.__awaitingApproval || input.ctx?.__awaitingUserQuestion
7586
7686
  );
7687
+ const resumed = Boolean(input.ctx?.__restoredFromSnapshot || pausedMidRun);
7688
+ delete ctx.__restoredFromSnapshot;
7587
7689
  const startedAt = Date.now();
7588
7690
  let sessionEnded = false;
7589
7691
  const endSession = async (status, payload) => {
@@ -7609,6 +7711,7 @@ function createAgent(opts) {
7609
7711
  const opened = await openPluginSession(runHost, {
7610
7712
  messages: initial.messages,
7611
7713
  resumed,
7714
+ pausedMidRun,
7612
7715
  config
7613
7716
  });
7614
7717
  if (opened.messages !== initial.messages) {
@@ -10265,6 +10368,11 @@ ${pluginAppend}`;
10265
10368
  const instance = {
10266
10369
  invoke: async (input, config) => {
10267
10370
  await ensurePluginSetup();
10371
+ if (input?.ctx?.__guardrailBlocked !== void 0) {
10372
+ const clearedCtx = { ...input.ctx };
10373
+ delete clearedCtx.__guardrailBlocked;
10374
+ input = { ...input, ctx: clearedCtx };
10375
+ }
10268
10376
  const sessionHolder = { value: input };
10269
10377
  const emitSessionEvent = (event) => {
10270
10378
  try {
@@ -10315,12 +10423,20 @@ ${pluginAppend}`;
10315
10423
  stateHolder: sessionHolder
10316
10424
  }
10317
10425
  };
10318
- const resumed = Boolean(
10319
- input.ctx?.__restoredFromSnapshot || input.ctx?.__paused || input.ctx?.__resumeStage || input.ctx?.__awaitingApproval || input.ctx?.__awaitingUserQuestion
10426
+ const pausedMidRun = Boolean(
10427
+ input.ctx?.__paused || input.ctx?.__resumeStage || input.ctx?.__awaitingApproval || input.ctx?.__awaitingUserQuestion
10320
10428
  );
10429
+ const resumed = Boolean(input.ctx?.__restoredFromSnapshot || pausedMidRun);
10430
+ if (input.ctx?.__restoredFromSnapshot) {
10431
+ const consumedCtx = { ...input.ctx };
10432
+ delete consumedCtx.__restoredFromSnapshot;
10433
+ input = { ...input, ctx: consumedCtx };
10434
+ sessionHolder.value = input;
10435
+ }
10321
10436
  const opened = await openPluginSession(runHost, {
10322
10437
  messages: input.messages || [],
10323
10438
  resumed,
10439
+ pausedMidRun,
10324
10440
  config
10325
10441
  });
10326
10442
  if (opened.denied) {
@@ -11110,7 +11226,7 @@ var HOOKS = {
11110
11226
  sessionStart: { implemented: true },
11111
11227
  userPromptSubmit: {
11112
11228
  implemented: true,
11113
- notes: "Fires once per run for a new user turn. Skipped on resume, where the transcript tail is a tool result rather than a user message."
11229
+ notes: "Fires once per run whenever the transcript tail is a NEW user turn \u2014 including a turn appended to a state that was restored from a snapshot. Skipped on a genuine resume, where the tail is a tool result rather than a user message."
11114
11230
  },
11115
11231
  preModelCall: {
11116
11232
  implemented: true,
@@ -11123,7 +11239,7 @@ var HOOKS = {
11123
11239
  },
11124
11240
  postToolUse: {
11125
11241
  implemented: true,
11126
- notes: "Raw output, before compression and the hard cap. Also fires on a tool ERROR. Does NOT fire for a call that parks the run."
11242
+ notes: "Raw output, before compression and the hard cap. Also fires on a tool ERROR, on a preToolUse `result` short-circuit and on a tool-cache hit (`input.source` says which). Does NOT fire for a call that parks the run."
11127
11243
  },
11128
11244
  preCompact: { implemented: true },
11129
11245
  postCompact: { implemented: true },
@@ -12107,6 +12223,12 @@ function auditLog(config = {}) {
12107
12223
  return "[unserializable]";
12108
12224
  }
12109
12225
  };
12226
+ const renderDetail = (detail) => {
12227
+ if (!detail || typeof detail !== "object" || Array.isArray(detail) || !("args" in detail)) return detail;
12228
+ const { args, ...rest } = detail;
12229
+ const rendered = renderArgs(args);
12230
+ return rendered === void 0 ? rest : { ...rest, args: rendered };
12231
+ };
12110
12232
  return {
12111
12233
  name: config.name ?? "audit-log",
12112
12234
  // Lowest number: runs first, so denials by later plugins are still recorded
@@ -12135,7 +12257,7 @@ function auditLog(config = {}) {
12135
12257
  at: (/* @__PURE__ */ new Date()).toISOString(),
12136
12258
  kind: "notification",
12137
12259
  status: kind,
12138
- detail
12260
+ detail: renderDetail(detail)
12139
12261
  });
12140
12262
  },
12141
12263
  sessionEnd: async ({ status }, ctx) => {