@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.d.cts CHANGED
@@ -451,6 +451,13 @@ interface HookMap {
451
451
  error?: Error;
452
452
  durationMs: number;
453
453
  executionId: string;
454
+ /**
455
+ * Where `output` came from. `tool` is a real execution (or its error);
456
+ * `hook` is a `preToolUse.result` short-circuit; `cache` is a hit on the
457
+ * tool's own result cache — that value already passed this hook once,
458
+ * when it was stored, so a redactor sees it twice.
459
+ */
460
+ source?: "tool" | "hook" | "cache";
454
461
  };
455
462
  output: {
456
463
  /** Rewritten output — redaction, normalization, schema coercion. */
package/dist/index.d.ts CHANGED
@@ -451,6 +451,13 @@ interface HookMap {
451
451
  error?: Error;
452
452
  durationMs: number;
453
453
  executionId: string;
454
+ /**
455
+ * Where `output` came from. `tool` is a real execution (or its error);
456
+ * `hook` is a `preToolUse.result` short-circuit; `cache` is a hit on the
457
+ * tool's own result cache — that value already passed this hook once,
458
+ * when it was stored, so a redactor sees it twice.
459
+ */
460
+ source?: "tool" | "hook" | "cache";
454
461
  };
455
462
  output: {
456
463
  /** Rewritten output — redaction, normalization, schema coercion. */
package/dist/index.mjs CHANGED
@@ -4591,18 +4591,60 @@ function createToolsNode(initialTools, opts) {
4591
4591
  toolStateRef.__currentToolCallId = toolCallId;
4592
4592
  delete toolStateRef.__awaitingApproval;
4593
4593
  }
4594
- let hookAskedForApproval = false;
4595
- let hookApprovalPrompt;
4596
- let hookResult;
4597
- if (host?.has("preToolUse")) {
4594
+ const stableJson = (value) => {
4595
+ try {
4596
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
4597
+ if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
4598
+ const keys = Object.keys(value).sort();
4599
+ return `{${keys.map((k) => JSON.stringify(k) + ":" + stableJson(value[k])).join(",")}}`;
4600
+ } catch {
4601
+ return String(value);
4602
+ }
4603
+ };
4604
+ const rejectByPolicy = (gateReason, gatedArgs) => {
4605
+ const reason = gateReason || `Tool call blocked by policy: ${toolName}`;
4606
+ push({ role: "tool", content: reason, tool_call_id: toolCallId, name: tc.name });
4607
+ toolHistory.push({
4608
+ executionId: nanoid(),
4609
+ toolName,
4610
+ args: gatedArgs,
4611
+ output: reason,
4612
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4613
+ tool_call_id: toolCallId,
4614
+ status: "rejected"
4615
+ });
4616
+ onEvent?.({ type: "tool_call", phase: "skipped", name: toolName, id: tc.id, args: gatedArgs, error: { message: reason } });
4617
+ recordTraceEvent(traceSession, {
4618
+ type: "tool_call",
4619
+ label: `Tool Denied - ${toolName}`,
4620
+ actor: { scope: "tool", name: toolName, role: "tool" },
4621
+ status: "skipped",
4622
+ toolDetails,
4623
+ toolExecutionId: toolCallId,
4624
+ sections: buildToolTraceSections({
4625
+ args: gatedArgs,
4626
+ executionId: toolCallId,
4627
+ output: reason,
4628
+ status: "skipped",
4629
+ summary: reason,
4630
+ toolDetails,
4631
+ toolName
4632
+ })
4633
+ });
4634
+ toolCount += 1;
4635
+ return { status: "error" };
4636
+ };
4637
+ const applyPreToolUseGate = async (candidate) => {
4638
+ if (!host?.has("preToolUse")) return { ok: true, args: candidate, ask: false };
4598
4639
  const gate = await host.runGate("preToolUse", {
4599
4640
  toolName,
4600
4641
  toolCallId,
4601
- args,
4642
+ args: candidate,
4602
4643
  tool,
4603
4644
  executionCount: countSuccessfulToolExecutions(toolHistory, toolName)
4604
4645
  });
4605
- if (gate.input.args !== args) {
4646
+ let next = candidate;
4647
+ if (gate.input.args !== candidate) {
4606
4648
  const revalidated = validateToolArgs(tool, gate.input.args);
4607
4649
  if (!revalidated.ok && resolved.toolResponses.schemaValidation === "strict") {
4608
4650
  const message = `Tool argument validation failed after a plugin rewrite for ${toolName}: ${revalidated.message}`;
@@ -4610,48 +4652,31 @@ function createToolsNode(initialTools, opts) {
4610
4652
  markToolFailure(message, toolName, toolCallId);
4611
4653
  onEvent?.({ type: "tool_call", phase: "error", name: toolName, id: tc.id, args: gate.input.args, error: { message } });
4612
4654
  toolCount += 1;
4613
- return { status: "error" };
4655
+ return { ok: false, outcome: { status: "error" } };
4614
4656
  }
4615
- args = revalidated.ok ? revalidated.value : gate.input.args;
4657
+ next = revalidated.ok ? revalidated.value : gate.input.args;
4616
4658
  }
4617
4659
  if (gate.decision === "deny") {
4618
- const reason = gate.reason || `Tool call blocked by policy: ${toolName}`;
4619
- push({ role: "tool", content: reason, tool_call_id: toolCallId, name: tc.name });
4620
- toolHistory.push({
4621
- executionId: nanoid(),
4622
- toolName,
4623
- args,
4624
- output: reason,
4625
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4626
- tool_call_id: toolCallId,
4627
- status: "rejected"
4628
- });
4629
- onEvent?.({ type: "tool_call", phase: "skipped", name: toolName, id: tc.id, args, error: { message: reason } });
4630
- recordTraceEvent(traceSession, {
4631
- type: "tool_call",
4632
- label: `Tool Denied - ${toolName}`,
4633
- actor: { scope: "tool", name: toolName, role: "tool" },
4634
- status: "skipped",
4635
- toolDetails,
4636
- toolExecutionId: toolCallId,
4637
- sections: buildToolTraceSections({
4638
- args,
4639
- executionId: toolCallId,
4640
- output: reason,
4641
- status: "skipped",
4642
- summary: reason,
4643
- toolDetails,
4644
- toolName
4645
- })
4646
- });
4647
- toolCount += 1;
4648
- return { status: "error" };
4649
- }
4650
- if (gate.decision === "ask") {
4651
- hookAskedForApproval = true;
4652
- hookApprovalPrompt = gate.approvalPrompt;
4660
+ return { ok: false, outcome: rejectByPolicy(gate.reason, next) };
4653
4661
  }
4654
- if (gate.shortCircuit !== void 0) hookResult = gate.shortCircuit;
4662
+ return {
4663
+ ok: true,
4664
+ args: next,
4665
+ ask: gate.decision === "ask",
4666
+ approvalPrompt: gate.approvalPrompt,
4667
+ result: gate.shortCircuit
4668
+ };
4669
+ };
4670
+ let hookAskedForApproval = false;
4671
+ let hookApprovalPrompt;
4672
+ let hookResult;
4673
+ {
4674
+ const gated = await applyPreToolUseGate(args);
4675
+ if (!gated.ok) return gated.outcome;
4676
+ args = gated.args;
4677
+ hookAskedForApproval = gated.ask;
4678
+ hookApprovalPrompt = gated.approvalPrompt;
4679
+ if (gated.result !== void 0) hookResult = gated.result;
4655
4680
  }
4656
4681
  const maxExecutionsPerRun = normalizeMaxExecutionsPerRun(tool.maxExecutionsPerRun);
4657
4682
  if (maxExecutionsPerRun !== null) {
@@ -4736,7 +4761,34 @@ function createToolsNode(initialTools, opts) {
4736
4761
  return { status: "rejected", approval: approvalEntry };
4737
4762
  }
4738
4763
  if (approvalEntry.status === "approved" && approvalEntry.approvedArgs !== void 0) {
4739
- args = approvalEntry.approvedArgs;
4764
+ const approvedArgs = approvalEntry.approvedArgs;
4765
+ if (stableJson(approvedArgs) !== stableJson(args)) {
4766
+ const settleRefused = (resolution) => {
4767
+ approvalEntry.metadata = { ...approvalEntry.metadata || {}, resolution };
4768
+ approvalEntry.status = "executed";
4769
+ approvalEntry.resolvedAt = (/* @__PURE__ */ new Date()).toISOString();
4770
+ pendingByCallId.set(toolCallId, approvalEntry);
4771
+ };
4772
+ const revalidated = validateToolArgs(tool, approvedArgs);
4773
+ if (!revalidated.ok && resolved.toolResponses.schemaValidation === "strict") {
4774
+ const message = `Tool argument validation failed for reviewer-edited arguments of ${toolName}: ${revalidated.message}`;
4775
+ push({ role: "tool", content: message, tool_call_id: toolCallId, name: tc.name });
4776
+ markToolFailure(message, toolName, toolCallId);
4777
+ settleRefused("invalid_args");
4778
+ onEvent?.({ type: "tool_call", phase: "error", name: toolName, id: tc.id, args: approvedArgs, error: { message } });
4779
+ toolCount += 1;
4780
+ return { status: "error", approval: approvalEntry };
4781
+ }
4782
+ const gated = await applyPreToolUseGate(revalidated.ok ? revalidated.value : approvedArgs);
4783
+ if (!gated.ok) {
4784
+ settleRefused("policy_denied");
4785
+ return { ...gated.outcome, approval: approvalEntry };
4786
+ }
4787
+ args = gated.args;
4788
+ if (gated.result !== void 0 && hookResult === void 0) hookResult = gated.result;
4789
+ } else {
4790
+ args = approvedArgs;
4791
+ }
4740
4792
  onEvent?.({ type: "tool_approval", status: "approved", id: approvalEntry.id, toolName: approvalEntry.toolName, toolCallId: approvalEntry.toolCallId, decidedBy: approvalEntry.decidedBy, comment: approvalEntry.comment });
4741
4793
  }
4742
4794
  }
@@ -4746,16 +4798,6 @@ function createToolsNode(initialTools, opts) {
4746
4798
  const cacheCfg = tool.cache;
4747
4799
  const cacheEnabled = cacheCfg === true || cacheCfg && typeof cacheCfg === "object";
4748
4800
  const cacheKeyFn = cacheEnabled && typeof cacheCfg === "object" ? cacheCfg.keyFn : void 0;
4749
- const stableJson = (value) => {
4750
- try {
4751
- if (value === null || typeof value !== "object") return JSON.stringify(value);
4752
- if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
4753
- const keys = Object.keys(value).sort();
4754
- return `{${keys.map((k) => JSON.stringify(k) + ":" + stableJson(value[k])).join(",")}}`;
4755
- } catch {
4756
- return String(value);
4757
- }
4758
- };
4759
4801
  const cacheKey = cacheEnabled ? `${toolName}|${cacheKeyFn ? cacheKeyFn(args) : stableJson(args)}` : void 0;
4760
4802
  const toolCache = state.toolCache = state.toolCache || {};
4761
4803
  let cachedHit = void 0;
@@ -4803,6 +4845,22 @@ function createToolsNode(initialTools, opts) {
4803
4845
  try {
4804
4846
  if (cachedHit !== void 0) {
4805
4847
  const executionId2 = nanoid();
4848
+ if (host?.has("postToolUse")) {
4849
+ const shortGate = await host.runGate("postToolUse", {
4850
+ toolName,
4851
+ toolCallId,
4852
+ args,
4853
+ output: cachedHit,
4854
+ durationMs: 0,
4855
+ executionId: executionId2,
4856
+ source: shortCircuitSource === "plugin" ? "hook" : "cache"
4857
+ });
4858
+ if (shortGate.decision === "deny") {
4859
+ cachedHit = shortGate.reason || `Tool output withheld by policy: ${toolName}`;
4860
+ } else if (shortGate.input.output !== cachedHit) {
4861
+ cachedHit = shortGate.input.output;
4862
+ }
4863
+ }
4806
4864
  const { output: compressedCachedHit, stats: cachedContextPilotStats } = maybeCompressToolOutput(toolName, cachedHit, executionId2);
4807
4865
  const responsePolicy2 = applyToolResponseHardCap(toolName, compressedCachedHit, executionId2, resolved);
4808
4866
  toolHistory.push({
@@ -4969,7 +5027,8 @@ function createToolsNode(initialTools, opts) {
4969
5027
  args,
4970
5028
  output,
4971
5029
  durationMs,
4972
- executionId
5030
+ executionId,
5031
+ source: "tool"
4973
5032
  });
4974
5033
  if (postGate.decision === "deny") {
4975
5034
  output = postGate.reason || `Tool output withheld by policy: ${toolName}`;
@@ -5064,7 +5123,8 @@ function createToolsNode(initialTools, opts) {
5064
5123
  output: message,
5065
5124
  error: error instanceof Error ? error : new Error(message),
5066
5125
  durationMs,
5067
- executionId
5126
+ executionId,
5127
+ source: "tool"
5068
5128
  });
5069
5129
  if (errorGate.decision === "deny") {
5070
5130
  message = errorGate.reason || `Tool error withheld by policy: ${toolName}`;
@@ -5740,7 +5800,14 @@ var DISALLOWED_CTX_KEYS = /* @__PURE__ */ new Set([
5740
5800
  // Session bookkeeping: a resumed run must open its OWN session, so these
5741
5801
  // must not travel in a snapshot.
5742
5802
  "__pluginRunStarted",
5743
- "__pluginRunEnded"
5803
+ "__pluginRunEnded",
5804
+ // Set by restoreSnapshot on the way OUT, consumed by the next invoke. Never
5805
+ // persisted: a snapshot of a restored run would otherwise mark every later
5806
+ // restore as "resumed" forever.
5807
+ "__restoredFromSnapshot",
5808
+ // A per-turn verdict. It describes the run that produced the snapshot, not
5809
+ // the run that will resume from it.
5810
+ "__guardrailBlocked"
5744
5811
  ]);
5745
5812
  var clone = (value) => {
5746
5813
  if (typeof globalThis.structuredClone === "function") {
@@ -6364,6 +6431,7 @@ var SLOT_NAMES = [
6364
6431
  "contextBuilder"
6365
6432
  ];
6366
6433
  var DECISION_RANK = { allow: 0, ask: 1, deny: 2 };
6434
+ var isHookDecision = (value) => typeof value === "string" && Object.prototype.hasOwnProperty.call(DECISION_RANK, value);
6367
6435
  function escalate(current, next) {
6368
6436
  return DECISION_RANK[next] > DECISION_RANK[current] ? next : current;
6369
6437
  }
@@ -6373,6 +6441,15 @@ var HookTimeoutError = class extends Error {
6373
6441
  this.name = "HookTimeoutError";
6374
6442
  }
6375
6443
  };
6444
+ var InvalidHookDecisionError = class extends Error {
6445
+ constructor(plugin, hook, value) {
6446
+ const shown = typeof value === "string" ? JSON.stringify(value) : String(value);
6447
+ super(
6448
+ `plugin "${plugin}" hook "${hook}" returned an unknown decision ${shown}; expected "allow", "ask" or "deny"`
6449
+ );
6450
+ this.name = "InvalidHookDecisionError";
6451
+ }
6452
+ };
6376
6453
  function withTimeout(value, ms, label) {
6377
6454
  if (!ms || !Number.isFinite(ms) || ms <= 0) return Promise.resolve(value);
6378
6455
  return new Promise((resolve, reject) => {
@@ -6662,10 +6739,18 @@ function createPluginHost(plugins = [], options = {}) {
6662
6739
  `plugin "${entry.plugin}" hook "${hook}"`
6663
6740
  );
6664
6741
  output = raw ?? void 0;
6742
+ if (output && typeof output === "object" && output.decision !== void 0 && !isHookDecision(output.decision)) {
6743
+ throw new InvalidHookDecisionError(entry.plugin, hook, output.decision);
6744
+ }
6665
6745
  } catch (err) {
6666
6746
  const message = err instanceof Error ? err.message : String(err);
6667
6747
  const timedOut = err instanceof HookTimeoutError;
6668
- logger.warn(`plugin "${entry.plugin}" hook "${hook}" failed:`, message);
6748
+ const badDecision = err instanceof InvalidHookDecisionError;
6749
+ if (badDecision) {
6750
+ logger.error(`plugin "${entry.plugin}" hook "${hook}" returned an invalid decision:`, message);
6751
+ } else {
6752
+ logger.warn(`plugin "${entry.plugin}" hook "${hook}" failed:`, message);
6753
+ }
6669
6754
  emitPluginEvent(entry, hook, {
6670
6755
  phase: timedOut ? "timeout" : "error",
6671
6756
  error: { message },
@@ -6677,8 +6762,9 @@ function createPluginHost(plugins = [], options = {}) {
6677
6762
  durationMs: performanceNow() - startedAt
6678
6763
  });
6679
6764
  if (entry.failureMode === "closed") {
6765
+ const cause = timedOut ? "timed out" : badDecision ? "invalid decision" : "hook error";
6680
6766
  result.decision = "deny";
6681
- result.reason = `${entry.plugin}: ${timedOut ? "timed out" : "hook error"} (fail-closed) \u2014 ${message}`;
6767
+ result.reason = `${entry.plugin}: ${cause} (fail-closed) \u2014 ${message}`;
6682
6768
  result.deniedBy = entry.plugin;
6683
6769
  result.input = current;
6684
6770
  return result;
@@ -6736,11 +6822,19 @@ function createPluginHost(plugins = [], options = {}) {
6736
6822
  if (output.metadata && typeof output.metadata === "object") {
6737
6823
  result.metadata = { ...result.metadata ?? {}, ...output.metadata };
6738
6824
  }
6739
- const decision = output.decision;
6825
+ let decision = output.decision;
6826
+ if (decision === "ask" && hook !== "preToolUse") {
6827
+ logger.warn(
6828
+ `plugin "${entry.plugin}" returned "ask" from "${hook}", which cannot pause for approval; treating it as "deny".`
6829
+ );
6830
+ decision = "deny";
6831
+ }
6740
6832
  const escalated = decision ? escalate(result.decision, decision) : result.decision;
6741
6833
  const escalatesHere = escalated !== result.decision;
6742
6834
  result.decision = escalated;
6743
- if (typeof output.reason === "string" && (escalatesHere || !result.reason)) {
6835
+ if (escalatesHere) {
6836
+ result.reason = typeof output.reason === "string" ? `${entry.plugin}: ${output.reason}` : void 0;
6837
+ } else if (typeof output.reason === "string" && !result.reason) {
6744
6838
  result.reason = `${entry.plugin}: ${output.reason}`;
6745
6839
  }
6746
6840
  if (mutatedHere) {
@@ -6834,7 +6928,7 @@ async function openPluginSession(runHost, params) {
6834
6928
  const appended = startGate.collected.systemPromptAppend ?? [];
6835
6929
  const systemPromptAppend = appended.length > 0 ? appended.join("\n\n") : void 0;
6836
6930
  const last = messages[messages.length - 1];
6837
- if (params.resumed || last?.role !== "user" || !runHost.has("userPromptSubmit")) {
6931
+ if (params.pausedMidRun || last?.role !== "user" || !runHost.has("userPromptSubmit")) {
6838
6932
  return { messages, systemPromptAppend };
6839
6933
  }
6840
6934
  const content = last.content;
@@ -7079,6 +7173,11 @@ function createAgent(opts) {
7079
7173
  delete nextCtx.__paused;
7080
7174
  state = { ...state, ctx: Object.keys(nextCtx).length > 0 ? nextCtx : void 0 };
7081
7175
  }
7176
+ if (state.ctx?.__restoredFromSnapshot) {
7177
+ const nextCtx = { ...state.ctx };
7178
+ delete nextCtx.__restoredFromSnapshot;
7179
+ state = { ...state, ctx: Object.keys(nextCtx).length > 0 ? nextCtx : void 0 };
7180
+ }
7082
7181
  let resumeStage = null;
7083
7182
  if (state.ctx?.__resumeStage) {
7084
7183
  const nextCtx = { ...state.ctx };
@@ -7353,7 +7452,7 @@ function createAgent(opts) {
7353
7452
  continue;
7354
7453
  }
7355
7454
  if (toolCalls.length === 0) {
7356
- if (soManager && !state.ctx?.__finalizedDueToStructuredOutput && !state.ctx?.__structuredOutputForceFinalize) {
7455
+ if (soManager && !state.ctx?.__finalizedDueToStructuredOutput && !state.ctx?.__structuredOutputForceFinalize && !state.ctx?.__guardrailBlocked) {
7357
7456
  if (soManager.strategy.kind === "native") {
7358
7457
  const assistantText = extractMessageText2(lastMsg);
7359
7458
  if (assistantText) {
@@ -7522,6 +7621,7 @@ function createAgent(opts) {
7522
7621
  delete ctx.__plugins;
7523
7622
  delete ctx.__applySystemPromptContribution;
7524
7623
  delete ctx.__pluginState;
7624
+ delete ctx.__guardrailBlocked;
7525
7625
  const inheritedSession = config?.__pluginSession;
7526
7626
  ctx.__runId = inheritedSession?.runId ?? traceSession?.sessionId ?? `run_${nanoid(10)}`;
7527
7627
  const stateHolder = inheritedSession?.stateHolder ?? { value: void 0 };
@@ -7571,9 +7671,11 @@ function createAgent(opts) {
7571
7671
  usage: input.usage || { perRequest: [], totals: {} }
7572
7672
  };
7573
7673
  stateHolder.value = initial;
7574
- const resumed = Boolean(
7575
- input.ctx?.__restoredFromSnapshot || input.ctx?.__paused || input.ctx?.__resumeStage || input.ctx?.__awaitingApproval || input.ctx?.__awaitingUserQuestion
7674
+ const pausedMidRun = Boolean(
7675
+ input.ctx?.__paused || input.ctx?.__resumeStage || input.ctx?.__awaitingApproval || input.ctx?.__awaitingUserQuestion
7576
7676
  );
7677
+ const resumed = Boolean(input.ctx?.__restoredFromSnapshot || pausedMidRun);
7678
+ delete ctx.__restoredFromSnapshot;
7577
7679
  const startedAt = Date.now();
7578
7680
  let sessionEnded = false;
7579
7681
  const endSession = async (status, payload) => {
@@ -7599,6 +7701,7 @@ function createAgent(opts) {
7599
7701
  const opened = await openPluginSession(runHost, {
7600
7702
  messages: initial.messages,
7601
7703
  resumed,
7704
+ pausedMidRun,
7602
7705
  config
7603
7706
  });
7604
7707
  if (opened.messages !== initial.messages) {
@@ -10255,6 +10358,11 @@ ${pluginAppend}`;
10255
10358
  const instance = {
10256
10359
  invoke: async (input, config) => {
10257
10360
  await ensurePluginSetup();
10361
+ if (input?.ctx?.__guardrailBlocked !== void 0) {
10362
+ const clearedCtx = { ...input.ctx };
10363
+ delete clearedCtx.__guardrailBlocked;
10364
+ input = { ...input, ctx: clearedCtx };
10365
+ }
10258
10366
  const sessionHolder = { value: input };
10259
10367
  const emitSessionEvent = (event) => {
10260
10368
  try {
@@ -10305,12 +10413,20 @@ ${pluginAppend}`;
10305
10413
  stateHolder: sessionHolder
10306
10414
  }
10307
10415
  };
10308
- const resumed = Boolean(
10309
- input.ctx?.__restoredFromSnapshot || input.ctx?.__paused || input.ctx?.__resumeStage || input.ctx?.__awaitingApproval || input.ctx?.__awaitingUserQuestion
10416
+ const pausedMidRun = Boolean(
10417
+ input.ctx?.__paused || input.ctx?.__resumeStage || input.ctx?.__awaitingApproval || input.ctx?.__awaitingUserQuestion
10310
10418
  );
10419
+ const resumed = Boolean(input.ctx?.__restoredFromSnapshot || pausedMidRun);
10420
+ if (input.ctx?.__restoredFromSnapshot) {
10421
+ const consumedCtx = { ...input.ctx };
10422
+ delete consumedCtx.__restoredFromSnapshot;
10423
+ input = { ...input, ctx: consumedCtx };
10424
+ sessionHolder.value = input;
10425
+ }
10311
10426
  const opened = await openPluginSession(runHost, {
10312
10427
  messages: input.messages || [],
10313
10428
  resumed,
10429
+ pausedMidRun,
10314
10430
  config
10315
10431
  });
10316
10432
  if (opened.denied) {
@@ -11100,7 +11216,7 @@ var HOOKS = {
11100
11216
  sessionStart: { implemented: true },
11101
11217
  userPromptSubmit: {
11102
11218
  implemented: true,
11103
- 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."
11219
+ 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."
11104
11220
  },
11105
11221
  preModelCall: {
11106
11222
  implemented: true,
@@ -11113,7 +11229,7 @@ var HOOKS = {
11113
11229
  },
11114
11230
  postToolUse: {
11115
11231
  implemented: true,
11116
- 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."
11232
+ 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."
11117
11233
  },
11118
11234
  preCompact: { implemented: true },
11119
11235
  postCompact: { implemented: true },
@@ -12097,6 +12213,12 @@ function auditLog(config = {}) {
12097
12213
  return "[unserializable]";
12098
12214
  }
12099
12215
  };
12216
+ const renderDetail = (detail) => {
12217
+ if (!detail || typeof detail !== "object" || Array.isArray(detail) || !("args" in detail)) return detail;
12218
+ const { args, ...rest } = detail;
12219
+ const rendered = renderArgs(args);
12220
+ return rendered === void 0 ? rest : { ...rest, args: rendered };
12221
+ };
12100
12222
  return {
12101
12223
  name: config.name ?? "audit-log",
12102
12224
  // Lowest number: runs first, so denials by later plugins are still recorded
@@ -12125,7 +12247,7 @@ function auditLog(config = {}) {
12125
12247
  at: (/* @__PURE__ */ new Date()).toISOString(),
12126
12248
  kind: "notification",
12127
12249
  status: kind,
12128
- detail
12250
+ detail: renderDetail(detail)
12129
12251
  });
12130
12252
  },
12131
12253
  sessionEnd: async ({ status }, ctx) => {