@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,40 @@
1
+ import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
2
+ import type { AssistantMessage, Context, Message } from "@oh-my-pi/pi-ai";
3
+ import type { SessionContext } from "../session/session-context.js";
4
+ import type { SecretObfuscator } from "./obfuscator.js";
5
+ /**
6
+ * Restore secret placeholders for local display. Only message kinds the model
7
+ * itself authored from obfuscated context carry placeholders — assistant
8
+ * content and the LLM-written branch/compaction summaries. User, developer, and
9
+ * tool-result messages are persisted with their literal text, so operator-authored
10
+ * placeholder-shaped text must survive untouched; those roles are never walked.
11
+ */
12
+ export declare function deobfuscateSessionContext(sessionContext: SessionContext, obfuscator: SecretObfuscator | undefined): SessionContext;
13
+ export declare function deobfuscateAgentMessages(obfuscator: SecretObfuscator, messages: AgentMessage[]): AgentMessage[];
14
+ /**
15
+ * Restore placeholders in assistant content: visible text and tool-call
16
+ * arguments/intent/rawBlock. Thinking and signatures are opaque
17
+ * provider-replay/hidden-reasoning data and pass through byte-identical.
18
+ */
19
+ export declare function deobfuscateAssistantContent(obfuscator: SecretObfuscator, content: AssistantMessage["content"]): AssistantMessage["content"];
20
+ /**
21
+ * Restore placeholders inside a tool call's arguments. Arguments are arbitrary
22
+ * model-authored JSON, so tool-call arguments are the ONLY place a recursive
23
+ * JSON walk runs.
24
+ */
25
+ export declare function deobfuscateToolArguments(obfuscator: SecretObfuscator, args: Record<string, unknown>): Record<string, unknown>;
26
+ /** Redact secrets inside a tool call's arguments (same JSON-walk exception as {@link deobfuscateToolArguments}). */
27
+ export declare function obfuscateToolArguments(obfuscator: SecretObfuscator, args: Record<string, unknown>, sharedRegexSecretValues?: ReadonlySet<string>): Record<string, unknown>;
28
+ /**
29
+ * Redact secrets from outbound messages. User messages, tool results, and
30
+ * user-authored developer messages (e.g. `@file` mentions) are obfuscated.
31
+ * Assistant replay content is re-obfuscated too, because session restoration
32
+ * expands keyed placeholders locally before the next provider request. Inline
33
+ * image bytes are never walked.
34
+ */
35
+ export declare function obfuscateMessages(obfuscator: SecretObfuscator, messages: Message[]): Message[];
36
+ /**
37
+ * Redact outbound provider context. Only conversation messages are rewritten;
38
+ * the static system prompt and tool schemas pass through unchanged.
39
+ */
40
+ export declare function obfuscateProviderContext(obfuscator: SecretObfuscator | undefined, context: Context): Context;
@@ -1,6 +1,3 @@
1
- import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
2
- import type { AssistantMessage, Context, Message } from "@oh-my-pi/pi-ai";
3
- import type { SessionContext } from "../session/session-context.js";
4
1
  export interface SecretEntry {
5
2
  type: "plain" | "regex";
6
3
  content: string;
@@ -15,75 +12,6 @@ export type JsonValue = string | number | boolean | null | JsonValue[] | {
15
12
  export type JsonRecord = {
16
13
  [key: string]: JsonValue | undefined;
17
14
  };
18
- /**
19
- * Whether a default (no custom `replacement`) replace-mode regex can never
20
- * safely redact a 1-2 char match: `findNonMatchingReplacement`'s bounded
21
- * search — the same search `#generateRegexReplacement` runs at match time —
22
- * finds no candidate the regex fails to re-match. This holds independent of
23
- * any actual per-install key: the search already exhausts every character in
24
- * `REPLACEMENT_CHARS` (the alphabet `buildKeyedReplacementRun` draws its
25
- * fallback marker from) plus punctuation and whitespace, so if none of those
26
- * escape the regex, no key-derived marker drawn from the same alphabet can
27
- * either — the marker is guaranteed to re-match too, making every such match
28
- * unresolvable: the fallback could only ever emit the raw matched text
29
- * unchanged. Probed with a value (`"\0".repeat(length)`) the bounded search
30
- * never treats as a real candidate, so the result depends only on the
31
- * regex's own matching behavior, not on this specific probe.
32
- */
33
- export declare function regexHasUnresolvableShortMatchFallback(regex: RegExp): boolean;
34
- export declare const MIN_OBFUSCATE_SECRET_LEN = 8;
35
- /** Normalize a friendly name into the model-visible placeholder prefix. */
36
- export declare function sanitizeSecretFriendlyName(name: string): string | undefined;
37
- /**
38
- * Whether an entry needs the persisted placeholder key: either because it can
39
- * produce a reversible (keyed) obfuscate-mode placeholder, or because a default
40
- * (no custom `replacement`) replace-mode regex can reach
41
- * `#generateRegexReplacement`'s key-derived idempotent fallback marker (see
42
- * `#generateReplacement`) when every same-length candidate re-matches a
43
- * pathological match-everything config (e.g. `[\s\S]{8}`). That fallback depends
44
- * on the persisted per-install key — not just length — to stay a fixed point
45
- * across a process restart; without a persisted key, a fresh install falls back
46
- * to a process-random key (`defaultPlaceholderKey()`), so the fallback marker
47
- * would churn across restarts even though the algorithm itself is stable. A
48
- * regex WITH a custom `replacement` never reaches that fallback (it always emits
49
- * the literal configured string), and a plain replace secret's replacement is
50
- * pure content-hash (`#generateSecretReplacement`), so neither needs the key.
51
- * Short plain obfuscate entries are toned down (never placeheld), so they must
52
- * NOT force key creation: otherwise a `secret-placeholder.key` file is written
53
- * and persisted for a config that ends up with no active secrets, leaving the
54
- * key readable via a tool and reusable for later placeholders.
55
- */
56
- export declare function secretEntryNeedsPlaceholderKey(entry: SecretEntry): boolean;
57
- /**
58
- * Whether a SET of entries needs the persisted placeholder key. `obfuscate()`
59
- * applies plain replace-mode mappings before the plain-obfuscate pass, so a plain
60
- * obfuscate entry only emits a reversible (keyed) placeholder when its content can
61
- * still appear AFTER the replace phase. When no obfuscate entry can ever produce a
62
- * placeholder, the persisted key must NOT be required/created — otherwise an
63
- * effectively replace-only secret set still writes `secret-placeholder.key` and
64
- * fails startup when the agent config dir is unwritable.
65
- *
66
- * The decision models the replace phase as the obfuscator actually runs it:
67
- * replace mappings are content-keyed (later duplicate wins) and applied in
68
- * descending content-length order; for a fresh probe (no prior placeholders) that
69
- * phase is plain sequential substring replacement. A plain obfuscate entry needs
70
- * the key when its content survives that simulated phase (direct typing) OR when
71
- * any effective replacement can form the content via tiling — a substring,
72
- * wholesale superstring, or prefix/suffix border that joins with surrounding
73
- * passthrough bytes (see `replacementCanFormContent`). This covers direct
74
- * shadowing (`SECRET -> safe`), reintroduction, duplicate ordering, transitive
75
- * chains, and context-joined fragments uniformly. Default (omitted) replacements
76
- * are deterministic, length-preserving, and distinct, so a same-content shadow
77
- * with no other interacting replacement stays key-free.
78
- * Replacement outputs are themselves rewritten by every later (shorter-content)
79
- * replacement before the plain-obfuscate pass sees them, so a fragment that a
80
- * subsequent replacement erases (`AA -> SEC` then `S -> X` turns every `SEC` into
81
- * `XEC`) no longer forces the key. Surrounding bytes stay modeled as arbitrary
82
- * passthrough, so testing the surviving fragment only drops false positives and
83
- * never under-approximates a real key need.
84
- */
85
- export declare function secretEntriesNeedPlaceholderKey(entries: SecretEntry[]): boolean;
86
- export declare function stripPendingSecretPlaceholderSuffix(text: string): string;
87
15
  export declare class SecretObfuscator {
88
16
  #private;
89
17
  constructor(entries: SecretEntry[], key?: string | (() => string));
@@ -99,39 +27,3 @@ export declare class SecretObfuscator {
99
27
  collectRegexSecretValuesForObfuscation(text: string): Set<string>;
100
28
  stripUnsafeFriendlyPlaceholderPrefixes(text: string, sharedRegexSecretValues: ReadonlySet<string>): string;
101
29
  }
102
- /**
103
- * Restore secret placeholders for local display. Only message kinds the model
104
- * itself authored from obfuscated context carry placeholders — assistant
105
- * content and the LLM-written branch/compaction summaries. User, developer, and
106
- * tool-result messages are persisted with their literal text, so operator-authored
107
- * placeholder-shaped text must survive untouched; those roles are never walked.
108
- */
109
- export declare function deobfuscateSessionContext(sessionContext: SessionContext, obfuscator: SecretObfuscator | undefined): SessionContext;
110
- export declare function deobfuscateAgentMessages(obfuscator: SecretObfuscator, messages: AgentMessage[]): AgentMessage[];
111
- /**
112
- * Restore placeholders in assistant content: visible text and tool-call
113
- * arguments/intent/rawBlock. Thinking and signatures are opaque
114
- * provider-replay/hidden-reasoning data and pass through byte-identical.
115
- */
116
- export declare function deobfuscateAssistantContent(obfuscator: SecretObfuscator, content: AssistantMessage["content"]): AssistantMessage["content"];
117
- /**
118
- * Restore placeholders inside a tool call's arguments. Arguments are arbitrary
119
- * model-authored JSON, so tool-call arguments are the ONLY place a recursive
120
- * JSON walk runs.
121
- */
122
- export declare function deobfuscateToolArguments(obfuscator: SecretObfuscator, args: Record<string, unknown>): Record<string, unknown>;
123
- /** Redact secrets inside a tool call's arguments (same JSON-walk exception as {@link deobfuscateToolArguments}). */
124
- export declare function obfuscateToolArguments(obfuscator: SecretObfuscator, args: Record<string, unknown>, sharedRegexSecretValues?: ReadonlySet<string>): Record<string, unknown>;
125
- /**
126
- * Redact secrets from outbound messages. User messages, tool results, and
127
- * user-authored developer messages (e.g. `@file` mentions) are obfuscated.
128
- * Assistant replay content is re-obfuscated too, because session restoration
129
- * expands keyed placeholders locally before the next provider request. Inline
130
- * image bytes are never walked.
131
- */
132
- export declare function obfuscateMessages(obfuscator: SecretObfuscator, messages: Message[]): Message[];
133
- /**
134
- * Redact outbound provider context. Only conversation messages are rewritten;
135
- * the static system prompt and tool schemas pass through unchanged.
136
- */
137
- export declare function obfuscateProviderContext(obfuscator: SecretObfuscator | undefined, context: Context): Context;
@@ -0,0 +1,95 @@
1
+ import type { JsonValue, SecretObfuscator } from "./obfuscator.js";
2
+ import { type RegexScanSegment, type ReplaceRegexScan } from "./placeholder.js";
3
+ export declare function transformOutsidePlaceholdersTracked(text: string, origin: string, shouldSkipPlaceholder: (placeholder: string) => boolean, transform: (chunk: string) => string, preservePlaceholder?: (placeholder: string) => string): {
4
+ text: string;
5
+ origin: string;
6
+ };
7
+ export declare function trailingOutsidePreservedPlaceholderChunk(text: string, shouldPreservePlaceholder: (placeholder: string) => boolean): string;
8
+ export declare function buildReplaceRegexScan(text: string, ranges: ReadonlyArray<{
9
+ start: number;
10
+ end: number;
11
+ }>, deobfuscateMap: ReadonlyMap<string, {
12
+ secret: string;
13
+ recursive: boolean;
14
+ }>): ReplaceRegexScan;
15
+ export declare function mapReplaceRegexMatch(segments: ReadonlyArray<RegexScanSegment>, scanStart: number, scanEnd: number): {
16
+ start: number;
17
+ end: number;
18
+ recursive: boolean;
19
+ preserveGeneratedPlaceholders: boolean;
20
+ partialPlaceholderCut: boolean;
21
+ cutResumeIndex: number;
22
+ firstPlaceholderScanStart: number;
23
+ };
24
+ /**
25
+ * Extend a scan-space resume position past a consecutive run of generated
26
+ * placeholder segments starting exactly at it, with no raw gap in between. A
27
+ * cut-resolution resume point that happens to land precisely on the START of
28
+ * ANOTHER placeholder must not stop there and hand it to a fresh `regex.exec`
29
+ * attempt — the same content, scanned as an opaque adjacent placeholder run,
30
+ * must resolve identically whether the run's LEADING member is still raw text
31
+ * (this call is about to placeholder it) or is ALREADY a placeholder from a
32
+ * prior call or an earlier pass of this same call. Without this, a bounded
33
+ * regex whose reach spans two adjacent secrets plus trailing spillover bytes
34
+ * (e.g. `[A-Z]{9}` over `ABCDEFGH` + `SECRETUV` + `A`) resolves the leading
35
+ * secret as its own independent redaction on the FIRST obfuscate() call (a
36
+ * genuinely raw prefix gets its own match, then the discard for the rest
37
+ * resumes right after it), but on a LATER call — once that prefix is itself a
38
+ * placeholder — the very first match attempt starts already inside the
39
+ * placeholder run, cannot be prefix-narrowed at all, and its discard resume
40
+ * point lands mid-run instead of past it, exposing a shorter tail (`SECRETUV`
41
+ * + `A`) to a clean, un-cut match the first call never attempted. Chaining the
42
+ * resume point through every immediately-adjacent placeholder makes both
43
+ * calls land on the exact same next scan position.
44
+ */
45
+ export declare function extendPastAdjacentPlaceholders(segments: ReadonlyArray<RegexScanSegment>, index: number): number;
46
+ export declare function redactWithFixedReplacementOutsidePlaceholders(text: string, origin: string, replacement: string, shouldPreservePlaceholder: (placeholder: string) => boolean): {
47
+ text: string;
48
+ origin: string;
49
+ };
50
+ export declare function deobfuscateGeneratedPlaceholderRanges(text: string, start: number, end: number, ranges: ReadonlyArray<{
51
+ start: number;
52
+ end: number;
53
+ }>, deobfuscateMap: ReadonlyMap<string, {
54
+ secret: string;
55
+ recursive: boolean;
56
+ }>): {
57
+ text: string;
58
+ recursive: boolean;
59
+ };
60
+ export declare function placeholderInnerText(text: string, start: number, end: number, ranges: ReadonlyArray<{
61
+ start: number;
62
+ end: number;
63
+ }>, deobfuscateMap: ReadonlyMap<string, {
64
+ secret: string;
65
+ recursive: boolean;
66
+ }>): string;
67
+ export declare function textOutsidePlaceholderRanges(text: string, start: number, end: number, ranges: ReadonlyArray<{
68
+ start: number;
69
+ end: number;
70
+ }>): string;
71
+ export declare function outsidePlaceholderRangesAnyIndependentlyMatch(text: string, scanText: string, segments: ReadonlyArray<RegexScanSegment>, start: number, end: number, ranges: ReadonlyArray<{
72
+ start: number;
73
+ end: number;
74
+ }>, regex: RegExp): boolean;
75
+ export declare function firstOutsidePlaceholderRange(start: number, end: number, ranges: ReadonlyArray<{
76
+ start: number;
77
+ end: number;
78
+ }>): {
79
+ start: number;
80
+ end: number;
81
+ } | undefined;
82
+ export declare function countOutsidePlaceholderRanges(start: number, end: number, ranges: ReadonlyArray<{
83
+ start: number;
84
+ end: number;
85
+ }>): number;
86
+ export declare function replaceRange(text: string, start: number, end: number, replacement: string): string;
87
+ /** Deep-walk an object, transforming all string values. */
88
+ export declare function deepWalkStrings<T>(obj: T, transform: (s: string) => string): T;
89
+ export declare function collectJsonRegexSecretValues(obfuscator: SecretObfuscator, value: JsonValue): Set<string>;
90
+ /**
91
+ * Map every string in arbitrary JSON. Used ONLY for tool-call arguments, whose
92
+ * shape is model-authored and not known ahead of time. No other caller may walk
93
+ * untyped data: every message/content path is handled by a typed transformer.
94
+ */
95
+ export declare function mapJsonStrings(value: JsonValue, fn: (s: string) => string): JsonValue;
@@ -0,0 +1,94 @@
1
+ import type { SecretEntry } from "./obfuscator.js";
2
+ export declare const MIN_OBFUSCATE_SECRET_LEN = 8;
3
+ export declare function defaultPlaceholderKey(): string;
4
+ type PlaceholderCaseHint = "U" | "L" | "C" | "M";
5
+ /** Normalize a friendly name into the model-visible placeholder prefix. */
6
+ export declare function sanitizeSecretFriendlyName(name: string): string | undefined;
7
+ /**
8
+ * Normalize a secret value into the same alnum-only, uppercased shape a
9
+ * friendly-name label or placeholder prefix is sanitized into, so comparing a
10
+ * raw (possibly lowercase/punctuated) secret value against already-sanitized,
11
+ * model-visible text does not miss a case- or separator-only variant. Unlike
12
+ * `sanitizeSecretFriendlyName` this never truncates and never signals "empty"
13
+ * via `undefined` — callers already guard on `.length > 0` before comparing.
14
+ */
15
+ export declare function sanitizeForCollisionCheck(value: string): string;
16
+ export declare function sanitizedLabelCollidesWithSecret(sanitizedLabel: string, sanitizedSecret: string): boolean;
17
+ /**
18
+ * Whether an entry needs the persisted placeholder key: either because it can
19
+ * produce a reversible (keyed) obfuscate-mode placeholder, or because a default
20
+ * (no custom `replacement`) replace-mode regex can reach
21
+ * `#generateRegexReplacement`'s key-derived idempotent fallback marker (see
22
+ * `#generateReplacement`) when every same-length candidate re-matches a
23
+ * pathological match-everything config (e.g. `[\s\S]{8}`). That fallback depends
24
+ * on the persisted per-install key — not just length — to stay a fixed point
25
+ * across a process restart; without a persisted key, a fresh install falls back
26
+ * to a process-random key (`defaultPlaceholderKey()`), so the fallback marker
27
+ * would churn across restarts even though the algorithm itself is stable. A
28
+ * regex WITH a custom `replacement` never reaches that fallback (it always emits
29
+ * the literal configured string), and a plain replace secret's replacement is
30
+ * pure content-hash (`#generateSecretReplacement`), so neither needs the key.
31
+ * Short plain obfuscate entries are toned down (never placeheld), so they must
32
+ * NOT force key creation: otherwise a `secret-placeholder.key` file is written
33
+ * and persisted for a config that ends up with no active secrets, leaving the
34
+ * key readable via a tool and reusable for later placeholders.
35
+ */
36
+ export declare function secretEntryNeedsPlaceholderKey(entry: SecretEntry): boolean;
37
+ /**
38
+ * Whether a SET of entries needs the persisted placeholder key. `obfuscate()`
39
+ * applies plain replace-mode mappings before the plain-obfuscate pass, so a plain
40
+ * obfuscate entry only emits a reversible (keyed) placeholder when its content can
41
+ * still appear AFTER the replace phase. When no obfuscate entry can ever produce a
42
+ * placeholder, the persisted key must NOT be required/created — otherwise an
43
+ * effectively replace-only secret set still writes `secret-placeholder.key` and
44
+ * fails startup when the agent config dir is unwritable.
45
+ *
46
+ * The decision models the replace phase as the obfuscator actually runs it:
47
+ * replace mappings are content-keyed (later duplicate wins) and applied in
48
+ * descending content-length order; for a fresh probe (no prior placeholders) that
49
+ * phase is plain sequential substring replacement. A plain obfuscate entry needs
50
+ * the key when its content survives that simulated phase (direct typing) OR when
51
+ * any effective replacement can form the content via tiling — a substring,
52
+ * wholesale superstring, or prefix/suffix border that joins with surrounding
53
+ * passthrough bytes (see `replacementCanFormContent`). This covers direct
54
+ * shadowing (`SECRET -> safe`), reintroduction, duplicate ordering, transitive
55
+ * chains, and context-joined fragments uniformly. Default (omitted) replacements
56
+ * are deterministic, length-preserving, and distinct, so a same-content shadow
57
+ * with no other interacting replacement stays key-free.
58
+ * Replacement outputs are themselves rewritten by every later (shorter-content)
59
+ * replacement before the plain-obfuscate pass sees them, so a fragment that a
60
+ * subsequent replacement erases (`AA -> SEC` then `S -> X` turns every `SEC` into
61
+ * `XEC`) no longer forces the key. Surrounding bytes stay modeled as arbitrary
62
+ * passthrough, so testing the surviving fragment only drops false positives and
63
+ * never under-approximates a real key need.
64
+ */
65
+ export declare function secretEntriesNeedPlaceholderKey(entries: SecretEntry[]): boolean;
66
+ export declare function buildHashBase(key: string, value: string): string;
67
+ export declare function buildKeyedReplacementRun(key: string, length: number): string;
68
+ export declare function inferCaseHint(secret: string): PlaceholderCaseHint | undefined;
69
+ export declare function buildPlaceholder(hint: PlaceholderCaseHint | undefined, base: string, friendlyName?: string): string;
70
+ /** Regex matching `$$HASH$$`, `$$HASH:U$$`, and `$$FRIENDLY_HASH(:hint)$$` placeholders. */
71
+ export declare const PLACEHOLDER_RE: RegExp;
72
+ export declare function resumePlaceholderScanAfterRejectedCandidate(match: RegExpExecArray): void;
73
+ export declare function placeholderWithoutFriendlyName(placeholder: string): string | undefined;
74
+ export declare function lookupFriendlyPlaceholderAlias(deobfuscateMap: ReadonlyMap<string, {
75
+ secret: string;
76
+ recursive: boolean;
77
+ }>, placeholder: string): {
78
+ secret: string;
79
+ recursive: boolean;
80
+ } | undefined;
81
+ export declare function stripPendingSecretPlaceholderSuffix(text: string): string;
82
+ export interface RegexScanSegment {
83
+ scanStart: number;
84
+ scanEnd: number;
85
+ textStart: number;
86
+ textEnd: number;
87
+ generatedPlaceholder: boolean;
88
+ recursive: boolean;
89
+ }
90
+ export interface ReplaceRegexScan {
91
+ text: string;
92
+ segments: RegexScanSegment[];
93
+ }
94
+ export {};
@@ -0,0 +1,82 @@
1
+ export declare const REPLACEMENT_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
2
+ /** Generate a deterministic same-length replacement string from a secret value. */
3
+ export declare function generateDeterministicReplacement(secret: string): string;
4
+ /**
5
+ * Force a length-preserving deterministic replacement to differ from the secret
6
+ * it stands in for. `generateDeterministicReplacement` seeds its first 1–2 chars
7
+ * with the `Z`/`ZZ` sentinel, so a whole configured value that is exactly `Z` or
8
+ * `ZZ` (or an astronomically unlikely longer hash collision) would otherwise be
9
+ * emitted unchanged and ship the raw secret to the provider. Flip the first char
10
+ * to a fixed different glyph: same length, still deterministic, guaranteed != the
11
+ * secret. Only safe for a whole CONFIGURED value (a plain secret matches its own
12
+ * literal, so the perturbed output is no longer matched and stays a fixed point);
13
+ * per-chunk remainders must keep the sentinel to remain idempotent across restart.
14
+ */
15
+ export declare function ensureDistinctReplacement(replacement: string, secret: string): string;
16
+ export interface RegexMatchContext {
17
+ /** Full text the match was found in (positions are offsets into it). */
18
+ text: string;
19
+ /** Start/end of the matched span being replaced. */
20
+ start: number;
21
+ end: number;
22
+ }
23
+ /**
24
+ * Whether `candidate`, substituted for the matched span in its surrounding text,
25
+ * is re-matched by `regex` at its own position. A replace-mode regex that depends
26
+ * on context (lookbehind/lookahead/`\b`) can match a candidate that does NOT match
27
+ * in isolation: e.g. `(?<=api=)[AZ]` never matches a bare `A`, but `api=A` does, so
28
+ * a candidate `A` chosen by an isolation test is re-redacted on the next obfuscate()
29
+ * pass and can oscillate back to the raw matched value. The probe substitutes the
30
+ * candidate into the FULL text — not a truncated window — so a wide lookbehind or
31
+ * lookahead (e.g. `(?<=A{600})`) still evaluates against the context that makes it
32
+ * match. Truncating that context dropped the assertion's reach and falsely
33
+ * accepted an oscillating, leaky candidate. The scan starts a bounded distance
34
+ * left of the span and stops once a match begins at/after the span's end (matches
35
+ * arrive in order), keeping per-candidate cost independent of total text length.
36
+ */
37
+ export declare function regexRematchesInContext(candidate: string, regex: RegExp, ctx: RegexMatchContext): boolean;
38
+ /**
39
+ * Search same-length replacements for one the regex does NOT match, so a default
40
+ * regex secret whose deterministic replacement collides with its own value (the
41
+ * `Z`/`ZZ` sentinel, or an astronomical hash collision) is still redacted to a
42
+ * STABLE nonmatching value instead of shipping the raw secret. A nonmatching
43
+ * candidate is a fixed point under re-obfuscation — the regex never re-matches it,
44
+ * so it cannot re-leak on a later pass. The search stays bounded to O(length *
45
+ * alphabet) regardless of value length: first exhaust every single-position
46
+ * substitution against a deterministic baseline (`AAAA…`, then `!AAA…`, `A!AA…`,
47
+ * …) so any regex that only needs one out-of-class byte — regardless of position —
48
+ * is found in a handful of probes rather than enumerating every combination (which
49
+ * for a 3-byte match-everything config, e.g. `[\s\S]{3}`, would otherwise run
50
+ * 90**3 = 729000 candidates through the regex on every single match, stalling
51
+ * provider requests). Candidates are enumerated deterministically over a stable
52
+ * ASCII alphabet: alphanumerics first (usually enough), then punctuation fallback
53
+ * bytes when the regex covers every alphanumeric candidate. When the regex still
54
+ * matches around a lone perturbed byte (for example `[A-Za-z0-9].*` matching the
55
+ * unperturbed tail), full-width same-byte candidates (`!!!!!`, `_____`, …) are
56
+ * tried next. When the regex covers every non-whitespace candidate (e.g. `\S{n}`),
57
+ * whitespace markers (a full space/tab run, then a single whitespace byte among
58
+ * non-whitespace filler) are tried as a last resort. A genuine match-everything
59
+ * regex (`.`/`[\s\S]`, which also matches space and tab) still exhausts this bounded
60
+ * sweep and returns undefined, letting the caller keep its own fixed-point fallback
61
+ * — bounded search can in principle miss an escape that depends jointly on
62
+ * multiple positions in a way no single-position swap reaches, but no realistic
63
+ * secret-redaction regex (character classes, literal matches, anchored/bounded
64
+ * repeats) has that shape.
65
+ */
66
+ export declare function findNonMatchingReplacement(value: string, regex: RegExp, context: RegexMatchContext): string | undefined;
67
+ /**
68
+ * Whether a default (no custom `replacement`) replace-mode regex can never
69
+ * safely redact a 1-2 char match: `findNonMatchingReplacement`'s bounded
70
+ * search — the same search `#generateRegexReplacement` runs at match time —
71
+ * finds no candidate the regex fails to re-match. This holds independent of
72
+ * any actual per-install key: the search already exhausts every character in
73
+ * `REPLACEMENT_CHARS` (the alphabet `buildKeyedReplacementRun` draws its
74
+ * fallback marker from) plus punctuation and whitespace, so if none of those
75
+ * escape the regex, no key-derived marker drawn from the same alphabet can
76
+ * either — the marker is guaranteed to re-match too, making every such match
77
+ * unresolvable: the fallback could only ever emit the raw matched text
78
+ * unchanged. Probed with a value (`"\0".repeat(length)`) the bounded search
79
+ * never treats as a real candidate, so the result depends only on the
80
+ * regex's own matching behavior, not on this specific probe.
81
+ */
82
+ export declare function regexHasUnresolvableShortMatchFallback(regex: RegExp): boolean;
@@ -2,7 +2,7 @@ import type { AgentEvent, ThinkingLevel } from "@oh-my-pi/pi-agent-core";
2
2
  import type { CompactionResult } from "@oh-my-pi/pi-agent-core/compaction";
3
3
  import type { Effort } from "@oh-my-pi/pi-ai";
4
4
  import type { Rule } from "../capability/rule.js";
5
- import type { RecoveredRetryError } from "../extensibility/shared-events.js";
5
+ import type { RetryErrorUpdate } from "../extensibility/shared-events.js";
6
6
  import type { Goal, GoalModeState } from "../goals/state.js";
7
7
  import type { ConfiguredThinkingLevel } from "../thinking.js";
8
8
  import type { TodoItem } from "../tools/todo.js";
@@ -40,7 +40,7 @@ export type AgentSessionEvent = Exclude<AgentEvent, {
40
40
  success: boolean;
41
41
  attempt: number;
42
42
  finalError?: string;
43
- recoveredErrors?: RecoveredRetryError[];
43
+ retryErrors?: RetryErrorUpdate[];
44
44
  } | {
45
45
  type: "retry_fallback_applied";
46
46
  from: string;
@@ -24,6 +24,12 @@ export declare const SHUTDOWN_CONSOLIDATE_BUDGET_MS = 1500;
24
24
  /** Options controlling session disposal. */
25
25
  export interface AgentSessionDisposeOptions {
26
26
  mnemopiConsolidateTimeoutMs?: number;
27
+ /**
28
+ * Deadline for the settle/drain wait before the terminal memory release
29
+ * (default 5s). The bounded-teardown paths (signal handlers, tests) may
30
+ * shorten it; late event handlers are still finalized after they settle.
31
+ */
32
+ drainTimeoutMs?: number;
27
33
  /**
28
34
  * Postmortem reason that triggered this dispose (signal/fatal teardown
29
35
  * paths). When set, the persisted `session_exit` diagnostic records it
@@ -41,7 +41,7 @@ import type { DaemonCompletionNotification } from "../launch/protocol.js";
41
41
  import { type MnemopiSessionState } from "../mnemopi/state.js";
42
42
  import { type PlanApprovalDetails } from "../plan-mode/approved-plan.js";
43
43
  import type { PlanModeState } from "../plan-mode/state.js";
44
- import { type SecretObfuscator } from "../secrets/obfuscator.js";
44
+ import type { SecretObfuscator } from "../secrets/obfuscator.js";
45
45
  import { type ConfiguredThinkingLevel } from "../thinking.js";
46
46
  import { type AskToolDetails, type AskToolInput } from "../tools/ask.js";
47
47
  import type { CheckpointState, CompletedRewindState } from "../tools/checkpoint.js";
@@ -83,7 +83,7 @@ export declare class AgentSession {
83
83
  /**
84
84
  * Arm prewalk outside the normal startup path so an explicit slash command starts immediately.
85
85
  */
86
- armPrewalk(target: Model, thinkingLevel?: ConfiguredThinkingLevel): void;
86
+ armPrewalk(target: Model, thinkingLevel?: ConfiguredThinkingLevel): boolean;
87
87
  /** Validate the active plan artifact and shape an `xd://propose` result for review-mode hosts. */
88
88
  preparePlanForReview(title: string): Promise<AgentToolResult<PlanApprovalDetails>>;
89
89
  constructor(config: AgentSessionConfig);
@@ -12,6 +12,7 @@ import type { OutputMeta } from "../tools/output-meta.js";
12
12
  export declare const SKILL_PROMPT_MESSAGE_TYPE = "skill-prompt";
13
13
  export declare const LSP_LATE_DIAGNOSTIC_MESSAGE_TYPE = "lsp-late-diagnostic";
14
14
  export declare const BACKGROUND_TAN_DISPATCH_MESSAGE_TYPE = "background-tan-dispatch";
15
+ export declare const PREWALK_PLAN_MESSAGE_TYPE = "prewalk-plan";
15
16
  /**
16
17
  * Logs provider-error turns so their actual cause is available outside the
17
18
  * session transcript. No-op for non-error stop reasons.
@@ -6,6 +6,8 @@ import { type ConfiguredThinkingLevel } from "../thinking.js";
6
6
  import type { PlanProposalHandler } from "../tools/resolve.js";
7
7
  import type { PlanYolo, Prewalk } from "./agent-session-types.js";
8
8
  import type { SessionManager } from "./session-manager.js";
9
+ /** Hidden plan steering is consumed within the live run and must not reappear after a context rebuild. */
10
+ export declare function isPrewalkPlanNudge(message: AgentMessage): boolean;
9
11
  /** Capabilities the prewalk coordinator borrows from its owning session. */
10
12
  export interface PrewalkCoordinatorHost {
11
13
  agent: Agent;
@@ -41,7 +43,7 @@ export declare class PrewalkCoordinator {
41
43
  /** Advances the one-way prewalk switch at a completed assistant-turn boundary. */
42
44
  advanceAtTurnEnd(liveMessages: AgentMessage[], context: AgentTurnEndContext | undefined): Promise<void>;
43
45
  /** Arms a prewalk immediately for an explicit slash-command request. */
44
- arm(target: Model, thinkingLevel?: ConfiguredThinkingLevel): void;
46
+ arm(target: Model, thinkingLevel?: ConfiguredThinkingLevel): boolean;
45
47
  /** Lazily enables plan-yolo's plan phase before the first prompt is built. */
46
48
  armPlanYoloIfNeeded(): Promise<void>;
47
49
  }
@@ -4,7 +4,7 @@ import type { Message, Model, ServiceTier, SimpleStreamOptions } from "@oh-my-pi
4
4
  import type { ModelRegistry } from "../config/model-registry.js";
5
5
  import type { Settings } from "../config/settings.js";
6
6
  import type { ExtensionRunner } from "../extensibility/extensions/index.js";
7
- import { type SecretObfuscator } from "../secrets/obfuscator.js";
7
+ import type { SecretObfuscator } from "../secrets/obfuscator.js";
8
8
  import type { HandoffResult, SessionHandoffOptions } from "./agent-session-types.js";
9
9
  import type { BashSessionTransition } from "./bash-runner.js";
10
10
  import type { SessionContext } from "./session-context.js";
@@ -55,9 +55,9 @@ export declare class SessionHandoff {
55
55
  #private;
56
56
  constructor(host: SessionHandoffHost);
57
57
  /**
58
- * Cancel in-progress handoff generation.
58
+ * Cancel in-progress handoff generation, preserving a harness-provided reason.
59
59
  */
60
- abortHandoff(): void;
60
+ abortHandoff(reason?: Error): void;
61
61
  /**
62
62
  * Check if handoff generation is in progress.
63
63
  */
@@ -133,6 +133,38 @@ export declare class SessionManager {
133
133
  flushSync(): void;
134
134
  /** Flush, then close the append writer. */
135
135
  close(): Promise<void>;
136
+ /**
137
+ * Raise the terminal write barrier ahead of the final {@link close}. Once
138
+ * sealed:
139
+ * - every later append, title change, and rewrite is a dropped no-op —
140
+ * including work an event handler tries to enqueue while dispose is
141
+ * awaiting `close()` on the disk tail;
142
+ * - the disk epoch is bumped, so queued-but-unexecuted tail work is
143
+ * superseded and an ALREADY-RUNNING fenced/repair rewrite (awaiting the
144
+ * tail, drain, writer close, or the atomic stage) fails its commit guard
145
+ * at the rename fence instead of publishing over a revived file.
146
+ * The final `close()` itself is scheduled after the bump and still runs;
147
+ * pre-seal hot-path appends are already in the page cache. Idempotent;
148
+ * terminal.
149
+ */
150
+ seal(): void;
151
+ /**
152
+ * Terminal release: drop the in-memory transcript and complete the
153
+ * {@link seal}. The entry journal and its index mirror the agent's message
154
+ * array (tool results, file contents, base64 frame images); on a disposed
155
+ * session — e.g. a parked subagent still referenced by the lifecycle
156
+ * adoption record — they would otherwise stay pinned for the process
157
+ * lifetime.
158
+ *
159
+ * Closes the append writer; with the seal up, nothing can reopen it. A
160
+ * revival may reopen the same JSONL through a NEW manager the moment
161
+ * dispose returns; a late event handler resuming on THIS manager must
162
+ * never race that writer — and a post-release rewrite would persist the
163
+ * now-empty entry list, truncating the transcript. Reads after this point
164
+ * reopen from disk (revival, `history://`). Only call from session
165
+ * dispose, after the final `close()`; idempotent.
166
+ */
167
+ releaseRetainedEntries(): void;
136
168
  getCwd(): string;
137
169
  /** Additional workspace directories beyond cwd (multi-root), absolute and normalized. */
138
170
  getAdditionalDirectories(): string[];
@@ -5,7 +5,7 @@ import type { ImageContent, Message, Model, SimpleStreamOptions } from "@oh-my-p
5
5
  import type { ModelRegistry } from "../config/model-registry.js";
6
6
  import type { Settings } from "../config/settings.js";
7
7
  import type { LocalProtocolOptions } from "../internal-urls/index.js";
8
- import { type SecretObfuscator } from "../secrets/obfuscator.js";
8
+ import type { SecretObfuscator } from "../secrets/obfuscator.js";
9
9
  import { type CustomMessage } from "./messages.js";
10
10
  import type { BuildSessionContextOptions, SessionContext } from "./session-context.js";
11
11
  import type { SessionManager } from "./session-manager.js";
@@ -108,6 +108,14 @@ export declare class SessionTools {
108
108
  get baseSystemPrompt(): string[];
109
109
  /** Replaces the controller-owned base prompt without applying it to the agent. */
110
110
  setBaseSystemPrompt(prompt: string[]): void;
111
+ /**
112
+ * Registers the per-turn `before_agent_start` system-prompt override and
113
+ * applies it to the agent. Base rebuilds during the turn preserve it until
114
+ * {@link clearTurnSystemPromptOverride}.
115
+ */
116
+ setTurnSystemPromptOverride(prompt: string[]): void;
117
+ /** Drops the active per-turn override; later rebuilds fall back to the base prompt. */
118
+ clearTurnSystemPromptOverride(): void;
111
119
  /** Skills currently rendered into the system prompt. */
112
120
  get skills(): Skill[];
113
121
  /** Diagnostics produced while loading the current skills. */
@@ -100,8 +100,13 @@ export declare class TurnRecovery {
100
100
  autoContinue: boolean;
101
101
  triggerContextTokens?: number;
102
102
  }): Promise<RecoveryCompactionResult>;
103
- /** Restores the configured primary after fallback cooldown expiry. */
104
- maybeRestoreRetryFallbackPrimary(): Promise<void>;
103
+ /**
104
+ * Restores the configured primary after fallback cooldown expiry.
105
+ * @returns true when the active model was actually switched back to the
106
+ * primary, so callers can re-run the pre-send context-fit check against the
107
+ * reverted (possibly smaller) window before issuing the next request.
108
+ */
109
+ maybeRestoreRetryFallbackPrimary(): Promise<boolean>;
105
110
  /** Applies model fallback policy from live usage health before a turn starts. */
106
111
  maybeApplyUsageAwareFallback(signal: AbortSignal, confirmer?: UsageFallbackConfirmer): Promise<boolean>;
107
112
  /** Applies automatic retry, credential rotation, and model fallback policy. */
@@ -151,9 +156,9 @@ export declare class TurnRecovery {
151
156
  */
152
157
  isRetryableReasonlessAbort(message: AssistantMessage): boolean;
153
158
  /**
154
- * Check if an error is retryable (transient errors or usage limits).
159
+ * Check if an error is retryable (transient errors, usage limits, or
160
+ * account-scoped policy denials that can rotate credentials).
155
161
  * Context overflow is NOT retryable (handled by compaction instead).
156
- * Usage-limit errors are retryable because the retry handler performs credential switching.
157
162
  */
158
163
  isRetryableError(message: AssistantMessage): boolean;
159
164
  /**
@@ -0,0 +1,2 @@
1
+ import type { SlashCommandSpec } from "./types.js";
2
+ export declare const BUILTIN_COLLABORATION_SLASH_COMMANDS: ReadonlyArray<SlashCommandSpec>;