@oh-my-pi/pi-coding-agent 17.2.10 → 17.2.12

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 (247) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/dist/{CHANGELOG-rhwzpexa.md → CHANGELOG-k9ghy5sn.md} +53 -0
  3. package/dist/cli.js +10775 -10762
  4. package/dist/types/advisor/runtime.d.ts +1 -1
  5. package/dist/types/capability/mcp.d.ts +11 -0
  6. package/dist/types/capability/skill.d.ts +6 -0
  7. package/dist/types/cli/command-help.d.ts +3 -0
  8. package/dist/types/commands/share.d.ts +25 -0
  9. package/dist/types/commit/agentic/index.d.ts +3 -1
  10. package/dist/types/commit/execute.d.ts +32 -0
  11. package/dist/types/commit/index.d.ts +2 -1
  12. package/dist/types/commit/pipeline.d.ts +7 -1
  13. package/dist/types/config/custom-models.d.ts +31 -0
  14. package/dist/types/config/model-config-values.d.ts +19 -0
  15. package/dist/types/config/model-patch.d.ts +93 -0
  16. package/dist/types/config/model-provider-discovery.d.ts +51 -0
  17. package/dist/types/config/model-registry.d.ts +9 -52
  18. package/dist/types/config/settings-schema.d.ts +1 -34
  19. package/dist/types/config/settings.d.ts +5 -3
  20. package/dist/types/debug/raw-sse-buffer.d.ts +9 -0
  21. package/dist/types/discovery/agent-plugin-format.d.ts +136 -0
  22. package/dist/types/discovery/agent-plugins.d.ts +1 -0
  23. package/dist/types/discovery/contained-path.d.ts +33 -0
  24. package/dist/types/discovery/index.d.ts +1 -0
  25. package/dist/types/eval/executor-base.d.ts +8 -2
  26. package/dist/types/eval/kernel-session-registry.d.ts +60 -0
  27. package/dist/types/export/share.d.ts +1 -1
  28. package/dist/types/extensibility/custom-tools/types.d.ts +2 -2
  29. package/dist/types/extensibility/extensions/loader.d.ts +7 -0
  30. package/dist/types/extensibility/extensions/types.d.ts +11 -0
  31. package/dist/types/extensibility/shared-events.d.ts +3 -2
  32. package/dist/types/extensibility/skills.d.ts +5 -0
  33. package/dist/types/lsp/diagnostics.d.ts +96 -0
  34. package/dist/types/lsp/index.d.ts +7 -128
  35. package/dist/types/lsp/servers.d.ts +72 -0
  36. package/dist/types/lsp/tool.d.ts +42 -0
  37. package/dist/types/lsp/workspace-diagnostics.d.ts +12 -0
  38. package/dist/types/lsp/writethrough.d.ts +33 -0
  39. package/dist/types/mcp/transports/header-policy.d.ts +46 -0
  40. package/dist/types/mcp/types.d.ts +15 -0
  41. package/dist/types/modes/components/agent-hub-projection.d.ts +2 -7
  42. package/dist/types/modes/components/agent-hub-renderer.d.ts +0 -7
  43. package/dist/types/modes/components/agent-hub.d.ts +2 -0
  44. package/dist/types/modes/components/agent-transcript-viewer.d.ts +2 -0
  45. package/dist/types/modes/components/chat-transcript-builder.d.ts +2 -0
  46. package/dist/types/modes/components/custom-editor.d.ts +1 -2
  47. package/dist/types/modes/components/status-line/types.d.ts +2 -0
  48. package/dist/types/modes/components/tool-execution.d.ts +2 -0
  49. package/dist/types/modes/theme/color.d.ts +19 -0
  50. package/dist/types/modes/theme/loader.d.ts +19 -0
  51. package/dist/types/modes/theme/schema.d.ts +175 -0
  52. package/dist/types/modes/theme/symbols.d.ts +28 -0
  53. package/dist/types/modes/theme/theme-class.d.ts +244 -0
  54. package/dist/types/modes/theme/theme.d.ts +9 -280
  55. package/dist/types/modes/theme/tui-adapters.d.ts +13 -0
  56. package/dist/types/modes/utils/transcript-render-helpers.d.ts +3 -2
  57. package/dist/types/registry/agent-registry.d.ts +1 -3
  58. package/dist/types/secrets/index.d.ts +26 -2
  59. package/dist/types/secrets/message-transform.d.ts +40 -0
  60. package/dist/types/secrets/obfuscator.d.ts +0 -108
  61. package/dist/types/secrets/placeholder-scan.d.ts +95 -0
  62. package/dist/types/secrets/placeholder.d.ts +94 -0
  63. package/dist/types/secrets/replacement.d.ts +82 -0
  64. package/dist/types/session/agent-session-events.d.ts +2 -2
  65. package/dist/types/session/agent-session-types.d.ts +6 -0
  66. package/dist/types/session/agent-session.d.ts +2 -2
  67. package/dist/types/session/messages.d.ts +1 -0
  68. package/dist/types/session/prewalk.d.ts +3 -1
  69. package/dist/types/session/session-handoff.d.ts +3 -3
  70. package/dist/types/session/session-manager.d.ts +32 -0
  71. package/dist/types/session/session-provider-boundary.d.ts +1 -1
  72. package/dist/types/session/session-tools.d.ts +8 -0
  73. package/dist/types/session/turn-recovery.d.ts +9 -4
  74. package/dist/types/slash-commands/builtin-collaboration.d.ts +2 -0
  75. package/dist/types/slash-commands/builtin-completions.d.ts +36 -0
  76. package/dist/types/slash-commands/builtin-control.d.ts +2 -0
  77. package/dist/types/slash-commands/builtin-lifecycle.d.ts +3 -0
  78. package/dist/types/slash-commands/builtin-marketplace.d.ts +11 -0
  79. package/dist/types/slash-commands/builtin-modes.d.ts +5 -0
  80. package/dist/types/slash-commands/builtin-registry.d.ts +1 -1
  81. package/dist/types/slash-commands/builtin-session.d.ts +2 -0
  82. package/dist/types/task/index.d.ts +2 -0
  83. package/dist/types/tools/gh-common.d.ts +69 -0
  84. package/dist/types/tools/gh-pr-checkout.d.ts +84 -0
  85. package/dist/types/tools/gh-pr-diff.d.ts +102 -0
  86. package/dist/types/tools/gh-run-watch.d.ts +78 -0
  87. package/dist/types/tools/gh-search.d.ts +61 -0
  88. package/dist/types/tools/gh-types.d.ts +340 -0
  89. package/dist/types/tools/gh-view.d.ts +88 -0
  90. package/dist/types/tools/gh.d.ts +4 -201
  91. package/dist/types/tools/read-archive.d.ts +16 -0
  92. package/dist/types/tools/read-format.d.ts +104 -0
  93. package/dist/types/tools/read-path-resolution.d.ts +19 -0
  94. package/dist/types/tools/read-pdf-images.d.ts +12 -0
  95. package/dist/types/tools/read-renderer.d.ts +24 -0
  96. package/dist/types/tools/read-selector.d.ts +27 -0
  97. package/dist/types/tools/read-sqlite.d.ts +16 -0
  98. package/dist/types/tools/read-summary.d.ts +19 -0
  99. package/dist/types/tools/read.d.ts +1 -23
  100. package/dist/types/tools/shell-tokenize.d.ts +18 -1
  101. package/dist/types/utils/changelog.d.ts +0 -5
  102. package/package.json +13 -13
  103. package/scripts/legacy-pi-virtual-module.ts +4 -1
  104. package/src/advisor/runtime.ts +12 -7
  105. package/src/capability/mcp.ts +11 -0
  106. package/src/capability/skill.ts +6 -0
  107. package/src/cli/command-help.ts +4 -0
  108. package/src/cli/gallery-cli.ts +1 -1
  109. package/src/cli-commands.ts +5 -0
  110. package/src/commands/commit.ts +16 -3
  111. package/src/commands/share.ts +71 -0
  112. package/src/commit/agentic/index.ts +39 -21
  113. package/src/commit/execute.ts +56 -0
  114. package/src/commit/index.ts +2 -1
  115. package/src/commit/pipeline.ts +20 -7
  116. package/src/config/custom-models.ts +188 -0
  117. package/src/config/model-config-values.ts +128 -0
  118. package/src/config/model-discovery.ts +1 -1
  119. package/src/config/model-patch.ts +252 -0
  120. package/src/config/model-provider-discovery.ts +132 -0
  121. package/src/config/model-registry.ts +84 -704
  122. package/src/config/settings-schema.ts +2 -33
  123. package/src/config/settings.ts +53 -3
  124. package/src/debug/raw-sse-buffer.ts +20 -0
  125. package/src/discovery/agent-plugin-format.ts +551 -0
  126. package/src/discovery/agent-plugins.ts +341 -0
  127. package/src/discovery/claude-plugins.ts +21 -5
  128. package/src/discovery/contained-path.ts +75 -0
  129. package/src/discovery/helpers.ts +23 -9
  130. package/src/discovery/index.ts +1 -0
  131. package/src/discovery/omp-plugins.ts +20 -7
  132. package/src/edit/hashline/diff.ts +5 -1
  133. package/src/eval/executor-base.ts +39 -6
  134. package/src/eval/jl/executor.ts +82 -371
  135. package/src/eval/kernel-session-registry.ts +398 -0
  136. package/src/eval/py/executor.ts +53 -261
  137. package/src/eval/rb/executor.ts +36 -279
  138. package/src/exec/non-interactive-env.ts +1 -0
  139. package/src/export/share.ts +2 -1
  140. package/src/extensibility/custom-tools/types.ts +2 -2
  141. package/src/extensibility/extensions/loader.ts +78 -14
  142. package/src/extensibility/extensions/runner.ts +6 -0
  143. package/src/extensibility/extensions/types.ts +12 -0
  144. package/src/extensibility/plugins/marketplace/manager.ts +2 -1
  145. package/src/extensibility/shared-events.ts +3 -2
  146. package/src/extensibility/skills.ts +9 -0
  147. package/src/internal-urls/skill-protocol.ts +14 -0
  148. package/src/internal-urls/vault-protocol.ts +2 -2
  149. package/src/launch/client.ts +11 -1
  150. package/src/launch/protocol.ts +7 -2
  151. package/src/lsp/diagnostics.ts +516 -0
  152. package/src/lsp/index.ts +20 -2819
  153. package/src/lsp/servers.ts +296 -0
  154. package/src/lsp/tool.ts +1352 -0
  155. package/src/lsp/workspace-diagnostics.ts +170 -0
  156. package/src/lsp/writethrough.ts +561 -0
  157. package/src/main.ts +3 -2
  158. package/src/mcp/config.ts +3 -0
  159. package/src/mcp/manager.ts +12 -9
  160. package/src/mcp/transports/header-policy.ts +95 -0
  161. package/src/mcp/transports/http.ts +35 -52
  162. package/src/mcp/transports/sse.ts +20 -27
  163. package/src/mcp/types.ts +15 -0
  164. package/src/modes/acp/acp-agent.ts +15 -4
  165. package/src/modes/components/agent-dashboard.ts +2 -0
  166. package/src/modes/components/agent-hub-projection.ts +2 -2
  167. package/src/modes/components/agent-hub-renderer.ts +3 -7
  168. package/src/modes/components/agent-hub.ts +5 -0
  169. package/src/modes/components/agent-transcript-viewer.ts +3 -0
  170. package/src/modes/components/chat-transcript-builder.ts +3 -0
  171. package/src/modes/components/custom-editor.ts +0 -9
  172. package/src/modes/components/status-line/component.ts +1 -0
  173. package/src/modes/components/status-line/segments.ts +6 -4
  174. package/src/modes/components/status-line/types.ts +2 -0
  175. package/src/modes/components/tool-execution.ts +13 -16
  176. package/src/modes/components/tree-selector.ts +62 -3
  177. package/src/modes/controllers/command-controller.ts +8 -2
  178. package/src/modes/controllers/event-controller.ts +15 -15
  179. package/src/modes/controllers/input-controller.ts +20 -2
  180. package/src/modes/controllers/selector-controller.ts +1 -0
  181. package/src/modes/theme/color.ts +133 -0
  182. package/src/modes/theme/loader.ts +178 -0
  183. package/src/modes/theme/schema.ts +263 -0
  184. package/src/modes/theme/symbols.ts +1000 -0
  185. package/src/modes/theme/theme-class.ts +611 -0
  186. package/src/modes/theme/theme.ts +27 -2453
  187. package/src/modes/theme/tui-adapters.ts +279 -0
  188. package/src/modes/utils/transcript-render-helpers.ts +4 -2
  189. package/src/modes/utils/ui-helpers.ts +1 -0
  190. package/src/registry/agent-registry.ts +2 -2
  191. package/src/sdk.ts +6 -49
  192. package/src/secrets/index.ts +51 -6
  193. package/src/secrets/message-transform.ts +287 -0
  194. package/src/secrets/obfuscator.ts +39 -1303
  195. package/src/secrets/placeholder-scan.ts +506 -0
  196. package/src/secrets/placeholder.ts +309 -0
  197. package/src/secrets/replacement.ts +216 -0
  198. package/src/session/agent-session-events.ts +2 -2
  199. package/src/session/agent-session-types.ts +6 -0
  200. package/src/session/agent-session.ts +146 -21
  201. package/src/session/indexed-session-storage.ts +7 -2
  202. package/src/session/messages.ts +1 -0
  203. package/src/session/prewalk.ts +57 -17
  204. package/src/session/session-advisors.ts +32 -34
  205. package/src/session/session-context.ts +3 -5
  206. package/src/session/session-handoff.ts +39 -16
  207. package/src/session/session-manager.ts +67 -3
  208. package/src/session/session-provider-boundary.ts +3 -6
  209. package/src/session/session-tools.ts +37 -3
  210. package/src/session/todo-tracker.ts +11 -1
  211. package/src/session/turn-recovery.ts +115 -84
  212. package/src/slash-commands/builtin-collaboration.ts +504 -0
  213. package/src/slash-commands/builtin-completions.ts +291 -0
  214. package/src/slash-commands/builtin-control.ts +78 -0
  215. package/src/slash-commands/builtin-lifecycle.ts +506 -0
  216. package/src/slash-commands/builtin-marketplace.ts +567 -0
  217. package/src/slash-commands/builtin-modes.ts +518 -0
  218. package/src/slash-commands/builtin-registry.ts +21 -2996
  219. package/src/slash-commands/builtin-session.ts +592 -0
  220. package/src/task/executor.ts +17 -14
  221. package/src/task/index.ts +21 -6
  222. package/src/tools/bash-skill-urls.ts +15 -0
  223. package/src/tools/bash.ts +10 -11
  224. package/src/tools/debug.ts +1 -1
  225. package/src/tools/gh-common.ts +288 -0
  226. package/src/tools/gh-pr-checkout.ts +679 -0
  227. package/src/tools/gh-pr-diff.ts +473 -0
  228. package/src/tools/gh-run-watch.ts +1015 -0
  229. package/src/tools/gh-search.ts +500 -0
  230. package/src/tools/gh-types.ts +379 -0
  231. package/src/tools/gh-view.ts +612 -0
  232. package/src/tools/gh.ts +109 -3821
  233. package/src/tools/jtd-to-json-schema.ts +123 -21
  234. package/src/tools/read-archive.ts +209 -0
  235. package/src/tools/read-format.ts +593 -0
  236. package/src/tools/read-path-resolution.ts +36 -0
  237. package/src/tools/read-pdf-images.ts +250 -0
  238. package/src/tools/read-renderer.ts +289 -0
  239. package/src/tools/read-selector.ts +84 -0
  240. package/src/tools/read-sqlite.ts +215 -0
  241. package/src/tools/read-summary.ts +199 -0
  242. package/src/tools/read.ts +82 -1824
  243. package/src/tools/shell-tokenize.ts +99 -1
  244. package/src/utils/changelog.ts +1 -1
  245. package/src/utils/title-generator.ts +3 -1
  246. package/src/web/search/providers/exa.ts +1 -1
  247. package/src/web/search/providers/zai.ts +12 -3
@@ -0,0 +1,287 @@
1
+ import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
2
+ import type { AssistantMessage, Context, ImageContent, Message, TextContent } from "@oh-my-pi/pi-ai";
3
+ import type { SessionContext } from "../session/session-context";
4
+ import type { JsonValue, SecretObfuscator } from "./obfuscator";
5
+ import { collectJsonRegexSecretValues, mapJsonStrings } from "./placeholder-scan";
6
+
7
+ // ═══════════════════════════════════════════════════════════════════════════
8
+ // Display restore (inbound, persisted/provider → local display)
9
+ // ═══════════════════════════════════════════════════════════════════════════
10
+
11
+ /**
12
+ * Restore secret placeholders for local display. Only message kinds the model
13
+ * itself authored from obfuscated context carry placeholders — assistant
14
+ * content and the LLM-written branch/compaction summaries. User, developer, and
15
+ * tool-result messages are persisted with their literal text, so operator-authored
16
+ * placeholder-shaped text must survive untouched; those roles are never walked.
17
+ */
18
+ export function deobfuscateSessionContext(
19
+ sessionContext: SessionContext,
20
+ obfuscator: SecretObfuscator | undefined,
21
+ ): SessionContext {
22
+ if (!obfuscator?.hasSecrets()) return sessionContext;
23
+ const messages = deobfuscateAgentMessages(obfuscator, sessionContext.messages);
24
+ return messages === sessionContext.messages ? sessionContext : { ...sessionContext, messages };
25
+ }
26
+
27
+ export function deobfuscateAgentMessages(obfuscator: SecretObfuscator, messages: AgentMessage[]): AgentMessage[] {
28
+ const deob = (text: string): string => obfuscator.deobfuscate(text);
29
+ let changed = false;
30
+ const result = messages.map((message): AgentMessage => {
31
+ switch (message.role) {
32
+ case "assistant": {
33
+ const content = deobfuscateAssistantContent(obfuscator, message.content);
34
+ if (content === message.content) return message;
35
+ changed = true;
36
+ return { ...message, content };
37
+ }
38
+ case "branchSummary": {
39
+ const summary = deob(message.summary);
40
+ if (summary === message.summary) return message;
41
+ changed = true;
42
+ return { ...message, summary };
43
+ }
44
+ case "compactionSummary": {
45
+ const summary = deob(message.summary);
46
+ const shortSummary = message.shortSummary === undefined ? undefined : deob(message.shortSummary);
47
+ const blocks = message.blocks === undefined ? undefined : deobfuscateTextBlocks(obfuscator, message.blocks);
48
+ if (summary === message.summary && shortSummary === message.shortSummary && blocks === message.blocks) {
49
+ return message;
50
+ }
51
+ changed = true;
52
+ return { ...message, summary, shortSummary, blocks };
53
+ }
54
+ default:
55
+ return message;
56
+ }
57
+ });
58
+ return changed ? result : messages;
59
+ }
60
+
61
+ /**
62
+ * Restore placeholders in assistant content: visible text and tool-call
63
+ * arguments/intent/rawBlock. Thinking and signatures are opaque
64
+ * provider-replay/hidden-reasoning data and pass through byte-identical.
65
+ */
66
+ export function deobfuscateAssistantContent(
67
+ obfuscator: SecretObfuscator,
68
+ content: AssistantMessage["content"],
69
+ ): AssistantMessage["content"] {
70
+ if (!obfuscator.hasSecrets()) return content;
71
+ const deob = (text: string): string => obfuscator.deobfuscate(text);
72
+ let changed = false;
73
+ const result = content.map((block): AssistantMessage["content"][number] => {
74
+ if (block.type === "text") {
75
+ const text = deob(block.text);
76
+ if (text === block.text) return block;
77
+ changed = true;
78
+ return { ...block, text };
79
+ }
80
+
81
+ if (block.type === "toolCall") {
82
+ const args = deobfuscateToolArguments(obfuscator, block.arguments);
83
+ const intent = block.intent === undefined ? undefined : deob(block.intent);
84
+ const rawBlock = block.rawBlock === undefined ? undefined : deob(block.rawBlock);
85
+ if (args === block.arguments && intent === block.intent && rawBlock === block.rawBlock) return block;
86
+ changed = true;
87
+ return { ...block, arguments: args, intent, rawBlock };
88
+ }
89
+ return block;
90
+ });
91
+ return changed ? result : content;
92
+ }
93
+
94
+ /**
95
+ * Restore placeholders inside a tool call's arguments. Arguments are arbitrary
96
+ * model-authored JSON, so tool-call arguments are the ONLY place a recursive
97
+ * JSON walk runs.
98
+ */
99
+ export function deobfuscateToolArguments(
100
+ obfuscator: SecretObfuscator,
101
+ args: Record<string, unknown>,
102
+ ): Record<string, unknown> {
103
+ if (!obfuscator.hasSecrets()) return args;
104
+ return mapJsonStrings(args as JsonValue, s => obfuscator.deobfuscate(s)) as Record<string, unknown>;
105
+ }
106
+
107
+ /** Redact secrets inside a tool call's arguments (same JSON-walk exception as {@link deobfuscateToolArguments}). */
108
+ export function obfuscateToolArguments(
109
+ obfuscator: SecretObfuscator,
110
+ args: Record<string, unknown>,
111
+ sharedRegexSecretValues?: ReadonlySet<string>,
112
+ ): Record<string, unknown> {
113
+ if (!obfuscator.hasSecrets()) return args;
114
+ const regexSecretValues = sharedRegexSecretValues ?? collectJsonRegexSecretValues(obfuscator, args as JsonValue);
115
+ return mapJsonStrings(args as JsonValue, s => obfuscator.obfuscate(s, regexSecretValues)) as Record<string, unknown>;
116
+ }
117
+
118
+ // ═══════════════════════════════════════════════════════════════════════════
119
+ // Outbound obfuscation (local → provider)
120
+ // ═══════════════════════════════════════════════════════════════════════════
121
+
122
+ type UserFacingMessage = Extract<Message, { role: "user" | "developer" | "toolResult" }>;
123
+
124
+ /** Obfuscate `text` blocks of a content array; image and other blocks pass through. */
125
+ function obfuscateTextBlocks(
126
+ obfuscator: SecretObfuscator,
127
+ content: (TextContent | ImageContent)[],
128
+ sharedRegexSecretValues?: ReadonlySet<string>,
129
+ ): (TextContent | ImageContent)[] {
130
+ let changed = false;
131
+ const result = content.map((block): TextContent | ImageContent => {
132
+ if (block.type !== "text") return block;
133
+ const text = obfuscator.obfuscate(block.text, sharedRegexSecretValues);
134
+ if (text === block.text) return block;
135
+ changed = true;
136
+ return { ...block, text };
137
+ });
138
+ return changed ? result : content;
139
+ }
140
+
141
+ /** Restore placeholders in `text` blocks of a content array; image and other blocks pass through. */
142
+ function deobfuscateTextBlocks(
143
+ obfuscator: SecretObfuscator,
144
+ content: (TextContent | ImageContent)[],
145
+ ): (TextContent | ImageContent)[] {
146
+ let changed = false;
147
+ const result = content.map((block): TextContent | ImageContent => {
148
+ if (block.type !== "text") return block;
149
+ const text = obfuscator.deobfuscate(block.text);
150
+ if (text === block.text) return block;
151
+ changed = true;
152
+ return { ...block, text };
153
+ });
154
+ return changed ? result : content;
155
+ }
156
+
157
+ /**
158
+ * Re-obfuscate assistant content before it returns to a provider after session
159
+ * restoration, removing friendly prefixes made unsafe by this batch. A changed
160
+ * thinking block loses its byte-bound replay signature.
161
+ */
162
+ function obfuscateAssistantContentForReplay(
163
+ obfuscator: SecretObfuscator,
164
+ content: AssistantMessage["content"],
165
+ sharedRegexSecretValues: ReadonlySet<string>,
166
+ ): AssistantMessage["content"] {
167
+ const obfuscate = (text: string): string =>
168
+ obfuscator.stripUnsafeFriendlyPlaceholderPrefixes(
169
+ obfuscator.obfuscate(text, sharedRegexSecretValues),
170
+ sharedRegexSecretValues,
171
+ );
172
+ let changed = false;
173
+ const result = content.map((block): AssistantMessage["content"][number] => {
174
+ if (block.type === "text") {
175
+ const text = obfuscate(block.text);
176
+ if (text === block.text) return block;
177
+ changed = true;
178
+ return { ...block, text };
179
+ }
180
+ if (block.type === "thinking") {
181
+ const thinking = obfuscate(block.thinking);
182
+ if (thinking === block.thinking) return block;
183
+ changed = true;
184
+ return { ...block, thinking, thinkingSignature: undefined };
185
+ }
186
+ if (block.type === "toolCall") {
187
+ const args = mapJsonStrings(block.arguments as JsonValue, obfuscate) as Record<string, unknown>;
188
+ const intent = block.intent === undefined ? undefined : obfuscate(block.intent);
189
+ const rawBlock = block.rawBlock === undefined ? undefined : obfuscate(block.rawBlock);
190
+ if (args === block.arguments && intent === block.intent && rawBlock === block.rawBlock) return block;
191
+ changed = true;
192
+ return { ...block, arguments: args, intent, rawBlock };
193
+ }
194
+ return block;
195
+ });
196
+ return changed ? result : content;
197
+ }
198
+
199
+ function collectMessageRegexSecretValues(obfuscator: SecretObfuscator, messages: Message[]): Set<string> {
200
+ const values = new Set<string>();
201
+ const addText = (text: string | undefined): void => {
202
+ if (text === undefined) return;
203
+ for (const value of obfuscator.collectRegexSecretValuesForObfuscation(text)) {
204
+ values.add(value);
205
+ }
206
+ };
207
+ for (const message of messages) {
208
+ if (message.role === "assistant") {
209
+ for (const block of message.content) {
210
+ if (block.type === "text") addText(block.text);
211
+ else if (block.type === "thinking") addText(block.thinking);
212
+ else if (block.type === "toolCall") {
213
+ for (const value of collectJsonRegexSecretValues(obfuscator, block.arguments as JsonValue)) {
214
+ values.add(value);
215
+ }
216
+ addText(block.intent);
217
+ addText(block.rawBlock);
218
+ }
219
+ }
220
+ continue;
221
+ }
222
+ if (
223
+ message.role !== "user" &&
224
+ message.role !== "toolResult" &&
225
+ !(message.role === "developer" && message.attribution === "user")
226
+ ) {
227
+ continue;
228
+ }
229
+ const target = message as UserFacingMessage;
230
+ if (typeof target.content === "string") {
231
+ addText(target.content);
232
+ continue;
233
+ }
234
+ for (const block of target.content) {
235
+ if (block.type === "text") addText(block.text);
236
+ }
237
+ }
238
+ return values;
239
+ }
240
+
241
+ /**
242
+ * Redact secrets from outbound messages. User messages, tool results, and
243
+ * user-authored developer messages (e.g. `@file` mentions) are obfuscated.
244
+ * Assistant replay content is re-obfuscated too, because session restoration
245
+ * expands keyed placeholders locally before the next provider request. Inline
246
+ * image bytes are never walked.
247
+ */
248
+ export function obfuscateMessages(obfuscator: SecretObfuscator, messages: Message[]): Message[] {
249
+ if (!obfuscator.hasSecrets()) return messages;
250
+ const sharedRegexSecretValues = collectMessageRegexSecretValues(obfuscator, messages);
251
+ let changed = false;
252
+ const result = messages.map((message): Message => {
253
+ if (
254
+ message.role !== "user" &&
255
+ message.role !== "toolResult" &&
256
+ !(message.role === "developer" && message.attribution === "user")
257
+ ) {
258
+ if (message.role !== "assistant") return message;
259
+ const content = obfuscateAssistantContentForReplay(obfuscator, message.content, sharedRegexSecretValues);
260
+ if (content === message.content) return message;
261
+ changed = true;
262
+ return { ...message, content };
263
+ }
264
+ const target = message as UserFacingMessage;
265
+ if (typeof target.content === "string") {
266
+ const content = obfuscator.obfuscate(target.content, sharedRegexSecretValues);
267
+ if (content === target.content) return message;
268
+ changed = true;
269
+ return { ...target, content } as Message;
270
+ }
271
+ const content = obfuscateTextBlocks(obfuscator, target.content, sharedRegexSecretValues);
272
+ if (content === target.content) return message;
273
+ changed = true;
274
+ return { ...target, content } as Message;
275
+ });
276
+ return changed ? result : messages;
277
+ }
278
+
279
+ /**
280
+ * Redact outbound provider context. Only conversation messages are rewritten;
281
+ * the static system prompt and tool schemas pass through unchanged.
282
+ */
283
+ export function obfuscateProviderContext(obfuscator: SecretObfuscator | undefined, context: Context): Context {
284
+ if (!obfuscator?.hasSecrets()) return context;
285
+ const messages = obfuscateMessages(obfuscator, context.messages);
286
+ return messages === context.messages ? context : { ...context, messages };
287
+ }