@namzu/sdk 62.14.0 → 63.0.0

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.
Files changed (142) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/dist/connector/mcp/stdio.d.ts.map +1 -1
  3. package/dist/connector/mcp/stdio.js +3 -0
  4. package/dist/connector/mcp/stdio.js.map +1 -1
  5. package/dist/eval/compare.d.ts +54 -0
  6. package/dist/eval/compare.d.ts.map +1 -1
  7. package/dist/eval/compare.js +146 -1
  8. package/dist/eval/compare.js.map +1 -1
  9. package/dist/eval/experiment.d.ts.map +1 -1
  10. package/dist/eval/experiment.js +46 -6
  11. package/dist/eval/experiment.js.map +1 -1
  12. package/dist/eval/index.d.ts +6 -2
  13. package/dist/eval/index.d.ts.map +1 -1
  14. package/dist/eval/index.js +3 -1
  15. package/dist/eval/index.js.map +1 -1
  16. package/dist/eval/report-stats.d.ts +6 -0
  17. package/dist/eval/report-stats.d.ts.map +1 -1
  18. package/dist/eval/report-stats.js +10 -0
  19. package/dist/eval/report-stats.js.map +1 -1
  20. package/dist/eval/run-options.d.ts +25 -0
  21. package/dist/eval/run-options.d.ts.map +1 -1
  22. package/dist/eval/run-options.js +36 -0
  23. package/dist/eval/run-options.js.map +1 -1
  24. package/dist/eval/run-summary.d.ts +8 -0
  25. package/dist/eval/run-summary.d.ts.map +1 -1
  26. package/dist/eval/run-summary.js +13 -3
  27. package/dist/eval/run-summary.js.map +1 -1
  28. package/dist/eval/settings.d.ts +74 -0
  29. package/dist/eval/settings.d.ts.map +1 -0
  30. package/dist/eval/settings.js +161 -0
  31. package/dist/eval/settings.js.map +1 -0
  32. package/dist/eval/split.d.ts +48 -0
  33. package/dist/eval/split.d.ts.map +1 -0
  34. package/dist/eval/split.js +41 -0
  35. package/dist/eval/split.js.map +1 -0
  36. package/dist/eval/types.d.ts +12 -0
  37. package/dist/eval/types.d.ts.map +1 -1
  38. package/dist/execution/local.d.ts.map +1 -1
  39. package/dist/execution/local.js +2 -1
  40. package/dist/execution/local.js.map +1 -1
  41. package/dist/peers/envelope.d.ts +11 -0
  42. package/dist/peers/envelope.d.ts.map +1 -1
  43. package/dist/peers/envelope.js +29 -0
  44. package/dist/peers/envelope.js.map +1 -1
  45. package/dist/peers/index.d.ts +3 -3
  46. package/dist/peers/index.d.ts.map +1 -1
  47. package/dist/peers/index.js +2 -2
  48. package/dist/peers/index.js.map +1 -1
  49. package/dist/peers/protocol.d.ts +26 -0
  50. package/dist/peers/protocol.d.ts.map +1 -1
  51. package/dist/peers/protocol.js +22 -2
  52. package/dist/peers/protocol.js.map +1 -1
  53. package/dist/peers/record.d.ts +2 -0
  54. package/dist/peers/record.d.ts.map +1 -1
  55. package/dist/plugin/hook-context.d.ts +30 -0
  56. package/dist/plugin/hook-context.d.ts.map +1 -0
  57. package/dist/plugin/hook-context.js +51 -0
  58. package/dist/plugin/hook-context.js.map +1 -0
  59. package/dist/plugin/index.d.ts +2 -1
  60. package/dist/plugin/index.d.ts.map +1 -1
  61. package/dist/plugin/index.js +1 -0
  62. package/dist/plugin/index.js.map +1 -1
  63. package/dist/plugin/lifecycle.d.ts +9 -0
  64. package/dist/plugin/lifecycle.d.ts.map +1 -1
  65. package/dist/plugin/lifecycle.js +13 -0
  66. package/dist/plugin/lifecycle.js.map +1 -1
  67. package/dist/plugin/shell-hook.d.ts +87 -12
  68. package/dist/plugin/shell-hook.d.ts.map +1 -1
  69. package/dist/plugin/shell-hook.js +224 -32
  70. package/dist/plugin/shell-hook.js.map +1 -1
  71. package/dist/process/session-child.d.ts +21 -0
  72. package/dist/process/session-child.d.ts.map +1 -0
  73. package/dist/process/session-child.js +32 -0
  74. package/dist/process/session-child.js.map +1 -0
  75. package/dist/public-runtime.d.ts +4 -3
  76. package/dist/public-runtime.d.ts.map +1 -1
  77. package/dist/public-runtime.js +4 -3
  78. package/dist/public-runtime.js.map +1 -1
  79. package/dist/public-types.d.ts +2 -2
  80. package/dist/public-types.d.ts.map +1 -1
  81. package/dist/runtime/query/executor/tool-call-admission.d.ts.map +1 -1
  82. package/dist/runtime/query/executor/tool-call-admission.js +2 -3
  83. package/dist/runtime/query/executor/tool-call-admission.js.map +1 -1
  84. package/dist/runtime/query/executor.d.ts +2 -0
  85. package/dist/runtime/query/executor.d.ts.map +1 -1
  86. package/dist/runtime/query/executor.js +13 -1
  87. package/dist/runtime/query/executor.js.map +1 -1
  88. package/dist/runtime/query/index.d.ts.map +1 -1
  89. package/dist/runtime/query/index.js +3 -2
  90. package/dist/runtime/query/index.js.map +1 -1
  91. package/dist/runtime/system-events.d.ts +3 -3
  92. package/dist/runtime/system-events.d.ts.map +1 -1
  93. package/dist/runtime/system-events.js +2 -1
  94. package/dist/runtime/system-events.js.map +1 -1
  95. package/dist/sandbox/provider/local.d.ts +1 -0
  96. package/dist/sandbox/provider/local.d.ts.map +1 -1
  97. package/dist/sandbox/provider/local.js +5 -2
  98. package/dist/sandbox/provider/local.js.map +1 -1
  99. package/dist/sandbox/terminal.d.ts.map +1 -1
  100. package/dist/sandbox/terminal.js +2 -1
  101. package/dist/sandbox/terminal.js.map +1 -1
  102. package/dist/tools/command-shell.d.ts.map +1 -1
  103. package/dist/tools/command-shell.js +7 -1
  104. package/dist/tools/command-shell.js.map +1 -1
  105. package/dist/types/message/index.d.ts +1 -1
  106. package/dist/types/message/index.d.ts.map +1 -1
  107. package/dist/types/message/index.js +1 -0
  108. package/dist/types/message/index.js.map +1 -1
  109. package/dist/types/plugin/index.d.ts +37 -10
  110. package/dist/types/plugin/index.d.ts.map +1 -1
  111. package/dist/types/plugin/index.js.map +1 -1
  112. package/package.json +1 -1
  113. package/src/connector/mcp/stdio.ts +3 -0
  114. package/src/eval/compare.ts +245 -2
  115. package/src/eval/experiment.ts +58 -4
  116. package/src/eval/index.ts +32 -1
  117. package/src/eval/report-stats.ts +11 -0
  118. package/src/eval/run-options.ts +51 -0
  119. package/src/eval/run-summary.ts +19 -1
  120. package/src/eval/settings.ts +225 -0
  121. package/src/eval/split.ts +83 -0
  122. package/src/eval/types.ts +12 -0
  123. package/src/execution/local.ts +2 -1
  124. package/src/peers/envelope.ts +36 -0
  125. package/src/peers/index.ts +3 -1
  126. package/src/peers/protocol.ts +24 -2
  127. package/src/plugin/hook-context.ts +58 -0
  128. package/src/plugin/index.ts +9 -0
  129. package/src/plugin/lifecycle.ts +13 -0
  130. package/src/plugin/shell-hook.ts +290 -43
  131. package/src/process/session-child.ts +38 -0
  132. package/src/public-runtime.ts +27 -0
  133. package/src/public-types.ts +2 -0
  134. package/src/runtime/query/executor/tool-call-admission.ts +2 -4
  135. package/src/runtime/query/executor.ts +15 -3
  136. package/src/runtime/query/index.ts +3 -3
  137. package/src/runtime/system-events.ts +2 -1
  138. package/src/sandbox/provider/local.ts +5 -1
  139. package/src/sandbox/terminal.ts +3 -1
  140. package/src/tools/command-shell.ts +7 -1
  141. package/src/types/message/index.ts +1 -0
  142. package/src/types/plugin/index.ts +48 -16
@@ -697,9 +697,15 @@ export {
697
697
 
698
698
  export {
699
699
  attachShellHooks,
700
+ boundHookContext,
701
+ boundHookReplacement,
700
702
  createShellHook,
701
703
  DEFAULT_SHELL_HOOK_TIMEOUT_MS,
702
704
  definePlugin,
705
+ frameOperatorHookContext,
706
+ MAX_HOOK_CONTEXT_CHARS,
707
+ MAX_HOOK_REPLACEMENT_CHARS,
708
+ sessionStartHookContext,
703
709
  discoverAllPluginDirs,
704
710
  discoverPlugins,
705
711
  assertEnableable,
@@ -1428,6 +1434,20 @@ export {
1428
1434
  loadEvalReportFile,
1429
1435
  summarizeRun,
1430
1436
  withEvalRunOptions,
1437
+ currentEvalSettings,
1438
+ evalSetting,
1439
+ DEFAULT_SETTINGS_NAME,
1440
+ buildSettingsSnapshot,
1441
+ defaultSettingsSnapshot,
1442
+ diffSettings,
1443
+ validateSettingsName,
1444
+ HELD_OUT_FRACTION,
1445
+ SPLIT_FILTERS,
1446
+ TASK_SPLIT_SEED,
1447
+ assignSplit,
1448
+ hashedSplit,
1449
+ taskId,
1450
+ validateSplitFilter,
1431
1451
  compareHarnessTrials,
1432
1452
  reviewHarnessCandidate,
1433
1453
  claimMismatchScorer,
@@ -1931,6 +1951,7 @@ export {
1931
1951
  MAX_PEER_CONNECTIONS,
1932
1952
  MAX_PEER_MESSAGE_TEXT_BYTES,
1933
1953
  MAX_PEER_REQUEST_BYTES,
1954
+ NoticeOutcomeSchema,
1934
1955
  NoticeRequestSchema,
1935
1956
  NoticeResponseSchema,
1936
1957
  OUTSTANDING_EXPIRY_MS,
@@ -1958,6 +1979,7 @@ export {
1958
1979
  createPeerEndpoint,
1959
1980
  derivePeerRef,
1960
1981
  formatPeerMessage,
1982
+ formatPersonMessage,
1961
1983
  formatPeerNotice,
1962
1984
  hardenPeerRuntimeDir,
1963
1985
  isPeerRecordLive,
@@ -1973,6 +1995,11 @@ export {
1973
1995
  udsPeerAddress,
1974
1996
  writePeerRecord,
1975
1997
  } from './peers/index.js'
1998
+ export {
1999
+ SESSION_CHILD_ENV,
2000
+ startedBySession,
2001
+ withSessionChildMarker,
2002
+ } from './process/session-child.js'
1976
2003
  export {
1977
2004
  ACCOUNTS_FILE_NAME,
1978
2005
  ACCOUNTS_FILE_VERSION,
@@ -244,6 +244,7 @@ export type {
244
244
  ShellHookOnFailure,
245
245
  ShellHookInput,
246
246
  ShellHookOptions,
247
+ ShellHookPathFilter,
247
248
  ShellHookOutcome,
248
249
  ShellHooksConfig,
249
250
  } from './plugin/shell-hook.js'
@@ -737,6 +738,7 @@ export type {
737
738
  DeliverRequest,
738
739
  DeliverResponse,
739
740
  DeliverStatus,
741
+ NoticeOutcome,
740
742
  NoticeRequest,
741
743
  NoticeResponse,
742
744
  PeerFrom,
@@ -1,4 +1,5 @@
1
1
  import { GENAI, NAMZU } from '../../../constants/telemetry/index.js'
2
+ import { boundHookContext } from '../../../plugin/hook-context.js'
2
3
  import { callableToolNames, formatToolNames } from '../../../registry/tool/callable.js'
3
4
  import { renderToolSchema } from '../../../registry/tool/schema.js'
4
5
  import type { ToolCall, ToolInputError } from '../../../types/message/index.js'
@@ -433,12 +434,9 @@ function interpretPreToolResults(
433
434
  */
434
435
  export function hookNote(texts: readonly string[]): string {
435
436
  const joined = texts.join('\n')
436
- return `[Hook note]\n${joined.length > MAX_HOOK_NOTE_CHARS ? `${joined.slice(0, MAX_HOOK_NOTE_CHARS)}\n[truncated]` : joined}`
437
+ return `[Hook note]\n${boundHookContext(joined)}`
437
438
  }
438
439
 
439
- /** A hook's text is one more thing for the model to read; it gets a bound. */
440
- const MAX_HOOK_NOTE_CHARS = 8_000
441
-
442
440
  /**
443
441
  * Turn the call the model issued into a name and a parsed input, giving
444
442
  * a configured repairer one chance to fix it first.
@@ -564,8 +564,14 @@ interface PostToolOverride {
564
564
  readonly output: string
565
565
  readonly isError: boolean
566
566
  readonly content?: ToolResultContent
567
+ /** A hook replaced a result that stood; the record says so beside it. */
568
+ readonly replacedByHook?: true
567
569
  }
568
570
 
571
+ /** What the record and the model are told beside a result a hook replaced. */
572
+ export const HOOK_REPLACED_RESULT_NOTE =
573
+ "A hook replaced this tool result: the text above is the hook's, not the tool's own output."
574
+
569
575
  /** What `pre_tool_use` hooks said about a call beyond running it or not. */
570
576
  export interface PreToolHookNotes {
571
577
  /** The strictest permission answer among the hooks: `ask` outranks `allow`. */
@@ -2944,9 +2950,12 @@ export class ToolExecutor {
2944
2950
  // receipt and the host's alike, framed as a hook's words. Added after
2945
2951
  // the output budget so a bounded result is never trimmed to make room
2946
2952
  // for it; the note has its own bound (`hookNote`).
2947
- const hookTexts = [preparedHookContext, post.context].filter(
2948
- (text): text is string => text !== undefined && text.length > 0,
2949
- )
2953
+ // The mark comes first so the bound on hook text never cuts it off.
2954
+ const hookTexts = [
2955
+ postOverride?.replacedByHook === true ? HOOK_REPLACED_RESULT_NOTE : undefined,
2956
+ preparedHookContext,
2957
+ post.context,
2958
+ ].filter((text): text is string => text !== undefined && text.length > 0)
2950
2959
  if (hookTexts.length > 0) {
2951
2960
  const note = hookNote(hookTexts)
2952
2961
  output = `${output}\n\n${note}`
@@ -3562,8 +3571,11 @@ export class ToolExecutor {
3562
3571
  override = {
3563
3572
  output: result.output,
3564
3573
  isError: false,
3574
+ ...(result.disclose === true ? { replacedByHook: true as const } : {}),
3565
3575
  ...(result.content !== undefined ? { content: result.content } : {}),
3566
3576
  }
3577
+ if (result.context) contexts.push(result.context)
3578
+ if (result.halt) this.requestHalt(result.halt.reason)
3567
3579
  continue
3568
3580
  // `retry` was a declared variant with no implementation: every
3569
3581
  // site that consumed it threw. Here it finally means something
@@ -18,6 +18,7 @@ import { restoreWorkingState, snapshotWorkingState } from '../../compaction/wire
18
18
  import { type CompactionConfig, CompactionConfigSchema } from '../../config/runtime.js'
19
19
  import { childSessionLog } from '../../manager/agent/child-session.js'
20
20
  import { type TurnRecorder, recordedTurnStart } from '../../manager/session/turn-recorder.js'
21
+ import { frameOperatorHookContext } from '../../plugin/hook-context.js'
21
22
  import { PromptContributionRegistry } from '../../prompt/contributions.js'
22
23
  import { resolveProviderCapabilities } from '../../provider/capabilities.js'
23
24
  import type { ProviderChainMember } from '../../provider/fallback.js'
@@ -2199,8 +2200,7 @@ export async function* query(params: QueryParams): AsyncGenerator<SessionEvent,
2199
2200
  promptContributions.replace({
2200
2201
  id: 'hooks:subagent_start',
2201
2202
  placement: 'context',
2202
- render: () =>
2203
- `Context from the operator's hooks:\n\n${startAnnotations.join('\n\n')}`,
2203
+ render: () => frameOperatorHookContext(startAnnotations),
2204
2204
  })
2205
2205
  }
2206
2206
  yield* eventTranslator.drainPending()
@@ -2226,7 +2226,7 @@ export async function* query(params: QueryParams): AsyncGenerator<SessionEvent,
2226
2226
  promptContributions.replace({
2227
2227
  id: 'hooks:user_prompt_submit',
2228
2228
  placement: 'context',
2229
- render: () => `Context from the operator's hooks:\n\n${annotations.join('\n\n')}`,
2229
+ render: () => frameOperatorHookContext(annotations),
2230
2230
  })
2231
2231
  }
2232
2232
  yield* eventTranslator.drainPending()
@@ -54,13 +54,14 @@ const SYSTEM_EVENT_STATUSES_LIST = [
54
54
  'refused',
55
55
  'idle',
56
56
  'exited',
57
+ 'delivered',
57
58
  ] as const
58
59
 
59
60
  /**
60
61
  * The closed set of outcomes this envelope may report.
61
62
  *
62
63
  * Not every status applies to every kind: `completed`/`failed`/`cancelled`/
63
- * `killed`/`interim` describe an `agent` or `job`; `queued`/`held`/`refused`
64
+ * `killed`/`interim` describe an `agent` or `job`; `queued`/`held`/`refused`/`delivered`
64
65
  * describe a peer message or its delivery notice; `idle`/`exited` describe a
65
66
  * peer's status notice. `formatSystemEvent` does not cross-check kind against
66
67
  * status — the caller owns that pairing — because the two closed sets belong
@@ -27,6 +27,7 @@ import {
27
27
  // registry needs the same one, and a near-copy would reproduce in the copy
28
28
  // every bug the original's comment was written to record.
29
29
  import { killTree } from '../../process/kill-tree.js'
30
+ import { SESSION_CHILD_ENV } from '../../process/session-child.js'
30
31
  import { resolveWithinAny, resolveWithinAnyReal } from '../../tools/paths.js'
31
32
  import type { SandboxId } from '../../types/ids/index.js'
32
33
  import type {
@@ -52,6 +53,7 @@ import {
52
53
  WINDOWS_CORE_ENV_KEYS,
53
54
  applyEnvironmentOverrides,
54
55
  pickEnvironmentEntries,
56
+ setEnvironmentEntry,
55
57
  } from '../../utils/process-environment.js'
56
58
  import { createTempDir, resolveTempBase } from '../../utils/temp.js'
57
59
  import { walkFilesLocally } from '../file-walk.js'
@@ -586,7 +588,7 @@ function buildSeatbeltArgs(
586
588
  // Environment building
587
589
  // ---------------------------------------------------------------------------
588
590
 
589
- function buildSafeEnv(
591
+ export function buildSafeEnv(
590
592
  configEnv?: Record<string, string>,
591
593
  optsEnv?: Record<string, string>,
592
594
  ): Record<string, string> {
@@ -598,6 +600,8 @@ function buildSafeEnv(
598
600
 
599
601
  applyEnvironmentOverrides(env, configEnv)
600
602
  applyEnvironmentOverrides(env, optsEnv)
603
+ // Last: a command in a sandbox is still a command a session started.
604
+ setEnvironmentEntry(env, SESSION_CHILD_ENV, '1')
601
605
 
602
606
  return env
603
607
  }
@@ -26,6 +26,8 @@
26
26
  * make teardown await every process it owns before using this primitive.
27
27
  */
28
28
 
29
+ import { withSessionChildMarker } from '../process/session-child.js'
30
+
29
31
  export interface TerminalSize {
30
32
  readonly cols: number
31
33
  readonly rows: number
@@ -172,7 +174,7 @@ export function openTerminalWith(
172
174
  cols: options.size.cols,
173
175
  rows: options.size.rows,
174
176
  cwd: options.cwd ?? defaults.cwd,
175
- ...(options.env ? { env: { ...options.env } } : {}),
177
+ env: withSessionChildMarker({ ...(options.env ?? process.env) }) as Record<string, string>,
176
178
  })
177
179
 
178
180
  const dataSub = child.onData((chunk) => {
@@ -56,6 +56,7 @@ import { constants, accessSync, existsSync } from 'node:fs'
56
56
  import { delimiter, isAbsolute, join, win32 } from 'node:path'
57
57
 
58
58
  import { MSYS_TAG_VARIABLE, registerMsysShell, registerProcessTag } from '../process/kill-tree.js'
59
+ import { withSessionChildMarker } from '../process/session-child.js'
59
60
  import type { ShellDialect } from '../types/tool/index.js'
60
61
 
61
62
  /** The shell a host-side command runs in. */
@@ -460,8 +461,13 @@ export function spawnHostShell(
460
461
  const windowsBash = shell.windows === true && shell.path !== undefined
461
462
  const tag =
462
463
  windowsBash || (shell.windows !== true && platform === 'linux') ? randomUUID() : undefined
464
+ // Every command the kernel runs on a session's behalf says so in its environment, and a program
465
+ // that must not be run by a model (`namzu send`) reads it. See `process/session-child.ts`.
466
+ const marked = withSessionChildMarker(spawned.env, platform)
463
467
  const resolved =
464
- tag === undefined ? spawned : { ...spawned, env: { ...spawned.env, [MSYS_TAG_VARIABLE]: tag } }
468
+ tag === undefined
469
+ ? { ...spawned, env: marked }
470
+ : { ...spawned, env: { ...marked, [MSYS_TAG_VARIABLE]: tag } }
465
471
  const common: SpawnOptions = {
466
472
  cwd: options.cwd,
467
473
  env: resolved.env,
@@ -365,6 +365,7 @@ export const RUNTIME_CONTEXT_MESSAGE_KINDS = [
365
365
  'limit-finalization',
366
366
  'peer-message',
367
367
  'peer-notice',
368
+ 'person-message',
368
369
  'progress-nudge',
369
370
  'reminder',
370
371
  'repeat-call',
@@ -96,7 +96,13 @@ export type PluginHookEvent =
96
96
  * to what the model is told (`annotate`).
97
97
  */
98
98
  | 'user_prompt_submit'
99
- /** A host's session opened or closed. Carries `sessionId` and no `turnId`: these hooks run outside any turn. */
99
+ /**
100
+ * A host's session opened or closed. Carries `sessionId` and no `turnId`:
101
+ * these hooks run outside any turn. `session_start` carries `source`;
102
+ * `session_end` carries `endReason`. A `session_start` hook may answer with
103
+ * `context` (on `continue`) or `annotate`: text the model reads, from the
104
+ * first turn of the session on.
105
+ */
100
106
  | 'session_start'
101
107
  | 'session_end'
102
108
  /** A compaction pass is about to run / has run. Carries `compaction`. */
@@ -110,11 +116,6 @@ export type PluginHookEvent =
110
116
  | 'subagent_start'
111
117
  /** A child session's turn ended. Fired after its own `turn_end`; carries `parentSessionId`, `parentTurnId` and `agentType`. */
112
118
  | 'subagent_stop'
113
- /**
114
- * A child session's turn is starting. Fired right after its own
115
- * `turn_start`; carries `parentSessionId` and `parentTurnId`. Observational.
116
- */
117
- | 'subagent_start'
118
119
  | 'turn_start'
119
120
  | 'turn_end'
120
121
  /**
@@ -273,6 +274,21 @@ export interface PluginNotificationInfo {
273
274
  readonly message: string
274
275
  }
275
276
 
277
+ /**
278
+ * Why a session started, on `session_start`: `startup` for a conversation
279
+ * with no earlier turn, `resume` for one that already had turns (a resumed
280
+ * conversation, a `--continue`, or a session rebuilt under a running one).
281
+ */
282
+ export type PluginSessionSource = 'startup' | 'resume'
283
+
284
+ /**
285
+ * Why a session closed, on `session_end`: `exit` when the host is finishing
286
+ * with it (the process ends, the person quits), `replaced` when the host
287
+ * builds another session in its place (a model or account switch, another
288
+ * conversation opened).
289
+ */
290
+ export type PluginSessionEndReason = 'exit' | 'replaced'
291
+
276
292
  export interface PluginHookContext {
277
293
  /** The turn the hook fired in. Absent on `session_start` and `session_end`. */
278
294
  readonly turnId?: TurnId
@@ -286,6 +302,10 @@ export interface PluginHookContext {
286
302
  readonly parentTurnId?: TurnId
287
303
  /** The name of the agent the child runs, on `subagent_start` and `subagent_stop`. */
288
304
  readonly agentType?: string
305
+ /** Why the session started, on `session_start`. */
306
+ readonly source?: PluginSessionSource
307
+ /** Why the session closed, on `session_end`. */
308
+ readonly endReason?: PluginSessionEndReason
289
309
  /** The operator's prompt, on `user_prompt_submit`. */
290
310
  readonly prompt?: string
291
311
  /** The pass, on `pre_compact` and `post_compact`. */
@@ -437,7 +457,18 @@ export type PluginHookResult =
437
457
  * hook redacting a secret that also appears in an image must — this variant
438
458
  * cannot inspect what it is preserving.
439
459
  */
440
- | { action: 'replace'; output: string; content?: ToolResultContent }
460
+ | ({
461
+ action: 'replace'
462
+ output: string
463
+ content?: ToolResultContent
464
+ /**
465
+ * Say, beside the result the model and the record carry, that a hook
466
+ * replaced it, so rewritten text is not passed off as the tool's own
467
+ * words. Off by default: a plugin that redacts quietly keeps doing so.
468
+ * A shell hook's replacement always sets it.
469
+ */
470
+ disclose?: boolean
471
+ } & PluginHookNotes)
441
472
 
442
473
  export function assertPluginHookResult(result: PluginHookResult): asserts result {
443
474
  const action = result.action
@@ -472,15 +503,16 @@ export type PluginHookResultFor<E extends PluginHookEvent> = E extends 'user_pro
472
503
  ? Extract<PluginHookResult, { action: 'continue' | 'skip' }>
473
504
  : E extends 'turn_end' | 'subagent_stop' | 'pre_compact'
474
505
  ? Extract<PluginHookResult, { action: 'continue' | 'skip' | 'error' }>
475
- : E extends
476
- | 'turn_interrupt'
477
- | 'session_start'
478
- | 'session_end'
479
- | 'notification'
480
- | 'turn_failure'
481
- | 'permission_denied'
482
- ? Extract<PluginHookResult, { action: 'continue' }>
483
- : Extract<PluginHookResult, { action: 'continue' | 'error' }>
506
+ : E extends 'session_start'
507
+ ? Extract<PluginHookResult, { action: 'continue' | 'annotate' }>
508
+ : E extends
509
+ | 'turn_interrupt'
510
+ | 'session_end'
511
+ | 'notification'
512
+ | 'turn_failure'
513
+ | 'permission_denied'
514
+ ? Extract<PluginHookResult, { action: 'continue' }>
515
+ : Extract<PluginHookResult, { action: 'continue' | 'error' }>
484
516
 
485
517
  /** The event and its handler verdict stay correlated, including in hook arrays. */
486
518
  export type PluginHookDefinition<E extends PluginHookEvent = PluginHookEvent> =