@oh-my-pi/pi-coding-agent 17.2.4 → 17.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +51 -0
- package/dist/{CHANGELOG-bpmhv26t.md → CHANGELOG-q2w43aw3.md} +51 -0
- package/dist/cli.js +4148 -3717
- package/dist/{template-8vdv6xb4.js → template-c2hyaytt.js} +8 -26
- package/dist/types/cli/args.d.ts +4 -1
- package/dist/types/cli/browser-relay-cli.d.ts +15 -0
- package/dist/types/cli/command-help.d.ts +99 -0
- package/dist/types/cli/help-extra.d.ts +2 -0
- package/dist/types/cli/thinking-levels.d.ts +5 -0
- package/dist/types/cli/update-cli.d.ts +13 -0
- package/dist/types/commands/browser-relay.d.ts +38 -0
- package/dist/types/commands/commit.d.ts +3 -0
- package/dist/types/commands/complete.d.ts +1 -1
- package/dist/types/commands/completions.d.ts +6 -0
- package/dist/types/commands/grep.d.ts +3 -0
- package/dist/types/commands/join.d.ts +4 -0
- package/dist/types/commands/launch-help.d.ts +171 -0
- package/dist/types/commands/launch.d.ts +5 -1
- package/dist/types/commands/models.d.ts +3 -0
- package/dist/types/commands/say.d.ts +10 -0
- package/dist/types/commands/worktree.d.ts +3 -0
- package/dist/types/config/keybindings.d.ts +5 -0
- package/dist/types/config/model-discovery.d.ts +2 -2
- package/dist/types/config/models-config-schema-bundle.d.ts +3 -0
- package/dist/types/config/models-config-schema.d.ts +2 -0
- package/dist/types/config/models-config.d.ts +1 -0
- package/dist/types/config/service-tier.d.ts +2 -0
- package/dist/types/config/settings-schema.d.ts +69 -21
- package/dist/types/cursor-bridge-tools.d.ts +1 -1
- package/dist/types/cursor.d.ts +7 -4
- package/dist/types/edit/index.d.ts +3 -3
- package/dist/types/edit/modes/replace.d.ts +22 -15
- package/dist/types/edit/renderer.d.ts +1 -1
- package/dist/types/eval/py/kernel.d.ts +3 -1
- package/dist/types/eval/py/spawn-options.d.ts +12 -16
- package/dist/types/extensibility/legacy-pi-ai-shim.d.ts +8 -1
- package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +1 -1
- package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +2 -0
- package/dist/types/launch/broker.d.ts +1 -1
- package/dist/types/launch/client.d.ts +6 -3
- package/dist/types/launch/ensure.d.ts +8 -0
- package/dist/types/launch/paths.d.ts +1 -1
- package/dist/types/launch/protocol.d.ts +2 -2
- package/dist/types/lsp/client.d.ts +2 -0
- package/dist/types/lsp/mux/daemon.d.ts +15 -0
- package/dist/types/lsp/mux/protocol.d.ts +57 -0
- package/dist/types/lsp/mux/server.d.ts +18 -0
- package/dist/types/lsp/types.d.ts +22 -2
- package/dist/types/main.d.ts +2 -0
- package/dist/types/mnemopi/embed-client.d.ts +1 -1
- package/dist/types/mnemopi/state.d.ts +0 -23
- package/dist/types/modes/components/assistant-message.d.ts +2 -0
- package/dist/types/modes/components/custom-editor.d.ts +2 -1
- package/dist/types/modes/components/read-tool-group.d.ts +2 -0
- package/dist/types/modes/components/stripped-tool-calls-placeholder.d.ts +12 -0
- package/dist/types/modes/components/tool-execution.d.ts +2 -0
- package/dist/types/modes/controllers/input-controller.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +1 -0
- package/dist/types/modes/types.d.ts +1 -0
- package/dist/types/modes/workflow.d.ts +2 -1
- package/dist/types/sdk.d.ts +3 -1
- package/dist/types/secrets/index.d.ts +1 -1
- package/dist/types/session/agent-session-types.d.ts +3 -1
- package/dist/types/session/indexed-session-storage.d.ts +1 -0
- package/dist/types/session/session-advisors.d.ts +1 -1
- package/dist/types/session/session-context.d.ts +2 -1
- package/dist/types/session/session-maintenance.d.ts +1 -0
- package/dist/types/session/session-manager.d.ts +16 -11
- package/dist/types/session/session-stats.d.ts +10 -0
- package/dist/types/session/session-storage.d.ts +15 -3
- package/dist/types/system-prompt.d.ts +4 -1
- package/dist/types/task/index.d.ts +2 -1
- package/dist/types/task/spawn-policy.d.ts +5 -0
- package/dist/types/thinking.d.ts +1 -7
- package/dist/types/tools/ast-grep.d.ts +1 -1
- package/dist/types/tools/browser/registry.d.ts +2 -1
- package/dist/types/tools/browser/relay/bridge.d.ts +35 -0
- package/dist/types/tools/browser/relay/daemon.d.ts +16 -0
- package/dist/types/tools/browser/relay/kind.d.ts +19 -0
- package/dist/types/tools/browser/relay/protocol.d.ts +97 -0
- package/dist/types/tools/browser/relay/server.d.ts +35 -0
- package/dist/types/tools/browser/render.d.ts +1 -0
- package/dist/types/tools/browser.d.ts +3 -0
- package/dist/types/tools/computer/exposure.d.ts +9 -7
- package/dist/types/tools/computer/protocol.d.ts +70 -17
- package/dist/types/tools/computer/supervisor.d.ts +23 -10
- package/dist/types/tools/computer/worker-entry.d.ts +1 -0
- package/dist/types/tools/computer/worker.d.ts +42 -7
- package/dist/types/tools/computer-renderer.d.ts +7 -5
- package/dist/types/tools/computer.d.ts +25 -44
- package/dist/types/tools/context.d.ts +3 -0
- package/dist/types/tools/glob.d.ts +1 -1
- package/dist/types/tools/grep.d.ts +1 -1
- package/dist/types/tools/render-utils.d.ts +2 -0
- package/dist/types/tools/{browser/run-cancellation.d.ts → run-scope.d.ts} +8 -8
- package/dist/types/tools/tool-timeouts.d.ts +5 -0
- package/dist/types/tools/xdev.d.ts +8 -0
- package/dist/types/utils/clipboard.d.ts +2 -2
- package/dist/types/web/kagi.d.ts +1 -0
- package/dist/types/web/search/index.d.ts +1 -1
- package/dist/types/web/search/providers/anthropic.d.ts +1 -0
- package/dist/types/web/search/providers/base.d.ts +2 -0
- package/dist/types/web/search/providers/brave.d.ts +1 -0
- package/dist/types/web/search/providers/browser-page.d.ts +1 -0
- package/dist/types/web/search/providers/codex.d.ts +1 -0
- package/dist/types/web/search/providers/exa.d.ts +1 -0
- package/dist/types/web/search/providers/firecrawl.d.ts +1 -0
- package/dist/types/web/search/providers/gemini.d.ts +1 -0
- package/dist/types/web/search/providers/jina.d.ts +1 -0
- package/dist/types/web/search/providers/kagi.d.ts +1 -0
- package/dist/types/web/search/providers/kimi.d.ts +1 -0
- package/dist/types/web/search/providers/parallel.d.ts +1 -0
- package/dist/types/web/search/providers/perplexity.d.ts +1 -0
- package/dist/types/web/search/providers/searxng.d.ts +1 -0
- package/dist/types/web/search/providers/tavily.d.ts +1 -0
- package/dist/types/web/search/providers/tinyfish.d.ts +1 -0
- package/dist/types/web/search/providers/utils.d.ts +6 -9
- package/dist/types/web/search/providers/zai.d.ts +1 -0
- package/dist/types/web/search/types.d.ts +4 -0
- package/package.json +12 -12
- package/src/cli/args.ts +7 -87
- package/src/cli/browser-relay-cli.ts +119 -0
- package/src/cli/command-help.ts +103 -0
- package/src/cli/completion-gen.ts +5 -5
- package/src/cli/flag-tables.ts +9 -0
- package/src/cli/help-extra.ts +89 -0
- package/src/cli/setup-cli.ts +13 -21
- package/src/cli/thinking-levels.ts +7 -0
- package/src/cli/update-cli.ts +26 -7
- package/src/cli-commands.ts +170 -33
- package/src/cli.ts +17 -5
- package/src/commands/acp.ts +3 -1
- package/src/commands/agents.ts +3 -2
- package/src/commands/auth-broker.ts +3 -2
- package/src/commands/auth-gateway.ts +3 -2
- package/src/commands/bench.ts +2 -3
- package/src/commands/browser-relay.ts +53 -0
- package/src/commands/cleanse.ts +2 -2
- package/src/commands/commit.ts +3 -2
- package/src/commands/complete.ts +2 -1
- package/src/commands/completions.ts +3 -2
- package/src/commands/config.ts +3 -2
- package/src/commands/dry-balance.ts +2 -2
- package/src/commands/gallery.ts +3 -2
- package/src/commands/gc.ts +3 -2
- package/src/commands/grep.ts +3 -2
- package/src/commands/grievances.ts +3 -2
- package/src/commands/install.ts +2 -2
- package/src/commands/join.ts +3 -2
- package/src/commands/launch-help.ts +116 -0
- package/src/commands/launch.ts +7 -191
- package/src/commands/models.ts +3 -2
- package/src/commands/plugin.ts +3 -2
- package/src/commands/read.ts +3 -2
- package/src/commands/say.ts +3 -2
- package/src/commands/setup.ts +3 -2
- package/src/commands/shell.ts +3 -2
- package/src/commands/ssh.ts +3 -2
- package/src/commands/stats.ts +3 -2
- package/src/commands/tiny-models.ts +2 -2
- package/src/commands/token.ts +2 -2
- package/src/commands/ttsr.ts +2 -2
- package/src/commands/update.ts +3 -2
- package/src/commands/usage.ts +3 -2
- package/src/commands/web-search.ts +3 -2
- package/src/commands/worktree.ts +3 -2
- package/src/config/keybindings.ts +5 -0
- package/src/config/model-discovery.ts +30 -13
- package/src/config/model-registry.ts +9 -5
- package/src/config/models-config-schema-bundle.ts +9 -0
- package/src/config/resolve-config-value.ts +2 -1
- package/src/config/service-tier.ts +5 -0
- package/src/config/settings-schema.ts +72 -20
- package/src/cursor-bridge-tools.ts +1 -1
- package/src/cursor.ts +10 -14
- package/src/edit/diff.ts +17 -8
- package/src/edit/index.ts +20 -7
- package/src/edit/modes/replace.ts +93 -40
- package/src/edit/renderer.ts +1 -1
- package/src/edit/streaming.ts +14 -17
- package/src/eval/py/kernel.ts +2 -1
- package/src/eval/py/spawn-options.ts +24 -29
- package/src/export/html/template.js +8 -26
- package/src/extensibility/extensions/wrapper.ts +1 -0
- package/src/extensibility/legacy-pi-ai-shim.ts +22 -1
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +7 -6
- package/src/extensibility/plugins/legacy-pi-compat.ts +124 -57
- package/src/extensibility/skills.ts +11 -0
- package/src/extensibility/utils.ts +10 -2
- package/src/launch/broker.ts +1 -1
- package/src/launch/client.ts +32 -13
- package/src/launch/ensure.ts +73 -0
- package/src/launch/paths.ts +1 -1
- package/src/launch/protocol.ts +2 -2
- package/src/lsp/client.ts +23 -2
- package/src/lsp/index.ts +8 -0
- package/src/lsp/mux/daemon.ts +348 -0
- package/src/lsp/mux/protocol.ts +96 -0
- package/src/lsp/mux/server.ts +797 -0
- package/src/lsp/types.ts +28 -2
- package/src/main.ts +7 -2
- package/src/mcp/oauth-flow.ts +11 -1
- package/src/mnemopi/embed-client.ts +49 -2
- package/src/mnemopi/state.ts +57 -23
- package/src/modes/components/assistant-message.ts +11 -0
- package/src/modes/components/chat-transcript-builder.ts +4 -0
- package/src/modes/components/custom-editor.ts +9 -0
- package/src/modes/components/read-tool-group.ts +10 -0
- package/src/modes/components/status-line/component.ts +1 -1
- package/src/modes/components/stripped-tool-calls-placeholder.ts +35 -0
- package/src/modes/components/tool-execution.ts +8 -0
- package/src/modes/components/tree-selector.ts +46 -48
- package/src/modes/controllers/event-controller.ts +17 -0
- package/src/modes/controllers/input-controller.ts +39 -1
- package/src/modes/controllers/mcp-command-controller.ts +11 -5
- package/src/modes/controllers/selector-controller.ts +20 -0
- package/src/modes/interactive-mode.ts +2 -0
- package/src/modes/types.ts +1 -0
- package/src/modes/utils/hotkeys-markdown.ts +1 -0
- package/src/modes/utils/interactive-context-helpers.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +6 -10
- package/src/modes/workflow.ts +8 -2
- package/src/prompts/agents/init.md +1 -1
- package/src/prompts/system/plan-mode-active.md +2 -2
- package/src/prompts/system/system-prompt.md +26 -52
- package/src/prompts/system/workflow-notice.md +3 -3
- package/src/prompts/tools/ast-grep.md +1 -1
- package/src/prompts/tools/bash.md +1 -1
- package/src/prompts/tools/browser.md +1 -0
- package/src/prompts/tools/computer.md +21 -21
- package/src/prompts/tools/glob.md +1 -1
- package/src/prompts/tools/grep.md +1 -1
- package/src/prompts/tools/replace.md +5 -4
- package/src/prompts/tools/task.md +4 -4
- package/src/sdk.ts +34 -6
- package/src/secrets/index.ts +1 -1
- package/src/session/agent-session-types.ts +3 -1
- package/src/session/agent-session.ts +13 -1
- package/src/session/indexed-session-storage.ts +9 -0
- package/src/session/prewalk.ts +24 -2
- package/src/session/session-advisors.ts +2 -2
- package/src/session/session-context.ts +16 -12
- package/src/session/session-dump-format.ts +1 -1
- package/src/session/session-maintenance.ts +38 -4
- package/src/session/session-manager.ts +102 -67
- package/src/session/session-paths.ts +118 -55
- package/src/session/session-stats.ts +48 -3
- package/src/session/session-storage.ts +30 -22
- package/src/session/stream-guards.ts +1 -1
- package/src/slash-commands/builtin-registry.ts +27 -24
- package/src/system-prompt.ts +8 -3
- package/src/task/index.ts +23 -8
- package/src/task/spawn-policy.ts +14 -0
- package/src/thinking.ts +2 -8
- package/src/tools/ast-grep.ts +10 -4
- package/src/tools/browser/cmux/cmux-tab.ts +5 -10
- package/src/tools/browser/cmux/rpc.ts +2 -8
- package/src/tools/browser/registry.ts +53 -2
- package/src/tools/browser/relay/bridge.ts +945 -0
- package/src/tools/browser/relay/daemon.ts +117 -0
- package/src/tools/browser/relay/extension-assets/background.js.txt +242 -0
- package/src/tools/browser/relay/extension-assets/manifest.json.txt +14 -0
- package/src/tools/browser/relay/extension-assets/options.html.txt +53 -0
- package/src/tools/browser/relay/extension-assets/options.js.txt +23 -0
- package/src/tools/browser/relay/kind.ts +41 -0
- package/src/tools/browser/relay/protocol.ts +54 -0
- package/src/tools/browser/relay/server.ts +141 -0
- package/src/tools/browser/render.ts +4 -1
- package/src/tools/browser/shared-daemon.ts +7 -58
- package/src/tools/browser/tab-supervisor.ts +5 -4
- package/src/tools/browser/tab-worker.ts +33 -12
- package/src/tools/browser.ts +26 -1
- package/src/tools/computer/exposure.ts +11 -35
- package/src/tools/computer/protocol.ts +51 -10
- package/src/tools/computer/supervisor.ts +227 -124
- package/src/tools/computer/worker-entry.ts +11 -6
- package/src/tools/computer/worker.ts +695 -81
- package/src/tools/computer-renderer.ts +104 -65
- package/src/tools/computer.ts +134 -428
- package/src/tools/context.ts +3 -0
- package/src/tools/glob.ts +9 -2
- package/src/tools/grep.ts +12 -6
- package/src/tools/inspect-image.ts +32 -6
- package/src/tools/render-utils.ts +2 -0
- package/src/tools/{browser/run-cancellation.ts → run-scope.ts} +11 -11
- package/src/tools/tool-timeouts.ts +1 -0
- package/src/tools/xdev.ts +29 -2
- package/src/utils/clipboard.ts +40 -14
- package/src/utils/command-args.ts +26 -28
- package/src/utils/tool-choice.ts +0 -13
- package/src/web/kagi.ts +2 -1
- package/src/web/search/index.ts +18 -2
- package/src/web/search/providers/anthropic.ts +4 -1
- package/src/web/search/providers/base.ts +2 -0
- package/src/web/search/providers/brave.ts +3 -1
- package/src/web/search/providers/browser-page.ts +6 -4
- package/src/web/search/providers/codex.ts +4 -1
- package/src/web/search/providers/duckduckgo.ts +2 -1
- package/src/web/search/providers/ecosia.ts +2 -1
- package/src/web/search/providers/exa.ts +4 -2
- package/src/web/search/providers/firecrawl.ts +3 -1
- package/src/web/search/providers/gemini.ts +8 -2
- package/src/web/search/providers/google.ts +2 -1
- package/src/web/search/providers/jina.ts +12 -2
- package/src/web/search/providers/kagi.ts +3 -0
- package/src/web/search/providers/kimi.ts +5 -1
- package/src/web/search/providers/mojeek.ts +2 -1
- package/src/web/search/providers/parallel.ts +5 -1
- package/src/web/search/providers/perplexity.ts +6 -3
- package/src/web/search/providers/public.ts +1 -1
- package/src/web/search/providers/searxng.ts +11 -5
- package/src/web/search/providers/startpage.ts +10 -4
- package/src/web/search/providers/synthetic.ts +11 -5
- package/src/web/search/providers/tavily.ts +3 -1
- package/src/web/search/providers/tinyfish.ts +3 -1
- package/src/web/search/providers/utils.ts +12 -10
- package/src/web/search/providers/xai.ts +2 -2
- package/src/web/search/providers/zai.ts +18 -3
- package/src/web/search/types.ts +6 -0
package/src/session/prewalk.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Agent, AgentMessage, AgentToolResult, AgentTurnEndContext } from "@oh-my-pi/pi-agent-core";
|
|
2
2
|
import { invalidateMessageCache } from "@oh-my-pi/pi-agent-core/compaction";
|
|
3
|
-
import type { Model } from "@oh-my-pi/pi-ai";
|
|
3
|
+
import type { Model, ToolResultMessage } from "@oh-my-pi/pi-ai";
|
|
4
4
|
import { prompt } from "@oh-my-pi/pi-utils";
|
|
5
5
|
import type { LocalProtocolOptions } from "../internal-urls";
|
|
6
6
|
import { resolveApprovedPlan } from "../plan-mode/approved-plan";
|
|
@@ -25,6 +25,28 @@ const PREWALK_ACTION_TOOLS: Record<string, true> = {
|
|
|
25
25
|
};
|
|
26
26
|
const PLAN_YOLO_HANDOFF_MESSAGE_TYPE = "plan-yolo-handoff";
|
|
27
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Whether a completed tool result is the first workspace-mutating action that
|
|
30
|
+
* arms the prewalk hand-off. A direct `edit`/`write` call always counts; a
|
|
31
|
+
* `write` that dispatched an `xd://` device (e.g. `lsp`, `ast_edit`, `debug`)
|
|
32
|
+
* counts only when the wrapped tool resolved to a `write`/`exec` approval tier.
|
|
33
|
+
* Read-only device calls — LSP navigation, `debug` inspection, `ast_edit` on
|
|
34
|
+
* internal URLs, help lookups — leave the tier `read` (or absent) and must not
|
|
35
|
+
* switch the model mid-investigation (issue #7312).
|
|
36
|
+
*/
|
|
37
|
+
function isPrewalkImplementationAction(result: ToolResultMessage): boolean {
|
|
38
|
+
if (!PREWALK_ACTION_TOOLS[result.toolName]) return false;
|
|
39
|
+
const details = result.details;
|
|
40
|
+
// A direct filesystem edit/write carries no `xd://` dispatch metadata.
|
|
41
|
+
if (!details || typeof details !== "object" || !("xdev" in details) || !details.xdev) return true;
|
|
42
|
+
const xdev = details.xdev;
|
|
43
|
+
// Device dispatch: switch only on a genuine mutation tier. An absent tier
|
|
44
|
+
// (help lookup, unresolved approval) declines the switch, matching the
|
|
45
|
+
// reporter's "stay on the large model a couple turns longer" preference.
|
|
46
|
+
if (typeof xdev !== "object" || !("tier" in xdev)) return false;
|
|
47
|
+
return xdev.tier === "write" || xdev.tier === "exec";
|
|
48
|
+
}
|
|
49
|
+
|
|
28
50
|
/** Capabilities the prewalk coordinator borrows from its owning session. */
|
|
29
51
|
export interface PrewalkCoordinatorHost {
|
|
30
52
|
agent: Agent;
|
|
@@ -100,7 +122,7 @@ export class PrewalkCoordinator {
|
|
|
100
122
|
|
|
101
123
|
const todoGateOpen = this.#todoSeen || !this.#host.getActiveToolNames().includes("todo");
|
|
102
124
|
const action = todoGateOpen
|
|
103
|
-
? context.toolResults.find(result =>
|
|
125
|
+
? context.toolResults.find(result => isPrewalkImplementationAction(result))
|
|
104
126
|
: undefined;
|
|
105
127
|
if (!action) {
|
|
106
128
|
if (!this.#planInjected) {
|
|
@@ -191,7 +191,7 @@ export interface SessionAdvisorsOptions {
|
|
|
191
191
|
/**
|
|
192
192
|
* Build the `replace`-mode `edit` a Cursor `pi_edit` frame needs. The
|
|
193
193
|
* advisor's own instance follows the configured `edit.mode` (`hashline` by
|
|
194
|
-
* default), whose schema the frame's `
|
|
194
|
+
* default), whose schema the frame's `old_string`/`new_string` args do not
|
|
195
195
|
* match, so without this every native advisor edit fails validation.
|
|
196
196
|
*/
|
|
197
197
|
createEditTool?(): AgentTool | undefined;
|
|
@@ -760,7 +760,7 @@ export class SessionAdvisors {
|
|
|
760
760
|
// to delete workspace files it was never granted (issue #5680 review).
|
|
761
761
|
const advisorCanMutateFiles = advisorToolMap.has("write") || advisorToolMap.has("edit");
|
|
762
762
|
if (advisorCanMutateFiles) availableAdvisorToolNames.add("delete");
|
|
763
|
-
// `pi_edit` speaks `replace`'s `
|
|
763
|
+
// `pi_edit` speaks `replace`'s `old_string`/`new_string` schema, which the
|
|
764
764
|
// advisor's ordinary `EditTool` (built at the session's configured
|
|
765
765
|
// `edit.mode`, `hashline` by default) does not accept. The bridge map
|
|
766
766
|
// swaps in a `replace` instance for the exec channel only — the
|
|
@@ -154,6 +154,21 @@ function snapcompactHistoryBlocksForContext(
|
|
|
154
154
|
return snapcompact.historyBlocks(archive, snapcompactHistoryBlockOptions(archive, options));
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
+
export function getOpenAiRemoteCompactionPayload(
|
|
158
|
+
compaction: CompactionEntry | null | undefined,
|
|
159
|
+
): ProviderPayload | undefined {
|
|
160
|
+
const candidate = compaction?.preserveData?.openaiRemoteCompaction;
|
|
161
|
+
if (!candidate || typeof candidate !== "object") return undefined;
|
|
162
|
+
const remote = candidate as { provider?: unknown; replacementHistory?: unknown };
|
|
163
|
+
if (typeof remote.provider !== "string" || remote.provider.length === 0) return undefined;
|
|
164
|
+
if (!Array.isArray(remote.replacementHistory)) return undefined;
|
|
165
|
+
return {
|
|
166
|
+
type: "openaiResponsesHistory",
|
|
167
|
+
provider: remote.provider,
|
|
168
|
+
items: remote.replacementHistory as Array<Record<string, unknown>>,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
157
172
|
export function buildSessionContext(
|
|
158
173
|
entries: SessionEntry[],
|
|
159
174
|
leafId?: string | null,
|
|
@@ -365,18 +380,7 @@ export function buildSessionContext(
|
|
|
365
380
|
}
|
|
366
381
|
}
|
|
367
382
|
} else if (compaction) {
|
|
368
|
-
const providerPayload
|
|
369
|
-
const candidate = compaction.preserveData?.openaiRemoteCompaction;
|
|
370
|
-
if (!candidate || typeof candidate !== "object") return undefined;
|
|
371
|
-
const remote = candidate as { provider?: unknown; replacementHistory?: unknown };
|
|
372
|
-
if (typeof remote.provider !== "string" || remote.provider.length === 0) return undefined;
|
|
373
|
-
if (!Array.isArray(remote.replacementHistory)) return undefined;
|
|
374
|
-
return {
|
|
375
|
-
type: "openaiResponsesHistory",
|
|
376
|
-
provider: remote.provider,
|
|
377
|
-
items: remote.replacementHistory as Array<Record<string, unknown>>,
|
|
378
|
-
};
|
|
379
|
-
})();
|
|
383
|
+
const providerPayload = getOpenAiRemoteCompactionPayload(compaction);
|
|
380
384
|
const remoteReplacementHistory = providerPayload?.items;
|
|
381
385
|
|
|
382
386
|
// Re-attach any archived snapcompact frames so the model can keep
|
|
@@ -82,7 +82,7 @@ function renderDumpHeader(options: FormatSessionDumpTextOptions, inventoryTools:
|
|
|
82
82
|
const hasSystemPromptToolInventory = options.inlineToolDescriptors === true;
|
|
83
83
|
if (inventoryTools.length > 0 && !hasSystemPromptToolInventory) {
|
|
84
84
|
lines.push("## Available Tools\n");
|
|
85
|
-
lines.push(renderToolInventory(inventoryTools
|
|
85
|
+
lines.push(renderToolInventory(inventoryTools));
|
|
86
86
|
lines.push("\n");
|
|
87
87
|
}
|
|
88
88
|
|
|
@@ -26,8 +26,10 @@ import {
|
|
|
26
26
|
DEFAULT_SHAKE_CONFIG,
|
|
27
27
|
effectiveReserveTokens,
|
|
28
28
|
estimateTokens,
|
|
29
|
+
hasContextTokenUsage,
|
|
29
30
|
NativeCompactionError,
|
|
30
31
|
prepareCompaction,
|
|
32
|
+
RESCUE_SHAKE_CONFIG,
|
|
31
33
|
resolveBudgetReserveTokens,
|
|
32
34
|
resolveThresholdTokens,
|
|
33
35
|
type ShakeConfig,
|
|
@@ -74,7 +76,7 @@ import {
|
|
|
74
76
|
resolveRoleModelFull,
|
|
75
77
|
} from "./role-models";
|
|
76
78
|
import type { SessionContext } from "./session-context";
|
|
77
|
-
import { getLatestCompactionEntry } from "./session-context";
|
|
79
|
+
import { getLatestCompactionEntry, getOpenAiRemoteCompactionPayload } from "./session-context";
|
|
78
80
|
import type { CompactionEntry, SessionEntry } from "./session-entries";
|
|
79
81
|
import type { SessionManager } from "./session-manager";
|
|
80
82
|
import type { ShakeMode, ShakeResult } from "./shake-types";
|
|
@@ -235,6 +237,7 @@ export interface SessionMaintenanceHost {
|
|
|
235
237
|
syncTodoPhasesFromBranch(): void;
|
|
236
238
|
resetAdvisorRuntimes(): void;
|
|
237
239
|
rebaseAfterCompaction(): void;
|
|
240
|
+
recordAnchoredHistoryRewrite(tokensRemoved: number): void;
|
|
238
241
|
getContextBreakdown(options?: {
|
|
239
242
|
contextWindow?: number;
|
|
240
243
|
pendingMessages?: AgentMessage[];
|
|
@@ -461,11 +464,12 @@ export class SessionMaintenance {
|
|
|
461
464
|
}
|
|
462
465
|
|
|
463
466
|
const branchEntries = this.#host.sessionManager.getBranch();
|
|
467
|
+
const latestCompaction = getLatestCompactionEntry(branchEntries);
|
|
464
468
|
const config = this.#withPlanProtection({
|
|
465
469
|
...(opts.config ?? AGGRESSIVE_SHAKE_CONFIG),
|
|
466
470
|
// Skip entries summarized away by the latest compaction — shaking them
|
|
467
471
|
// only churns persisted history with no prompt/cache effect.
|
|
468
|
-
keepBoundaryId:
|
|
472
|
+
keepBoundaryId: latestCompaction?.firstKeptEntryId,
|
|
469
473
|
});
|
|
470
474
|
const regions = collectShakeRegions(branchEntries, config);
|
|
471
475
|
if (regions.length === 0) {
|
|
@@ -475,20 +479,50 @@ export class SessionMaintenance {
|
|
|
475
479
|
const artifactId = await this.#saveShakeArtifact(regions);
|
|
476
480
|
const replacements = regions.map((region, index) => this.#shakeElidePlaceholder(region, index, artifactId));
|
|
477
481
|
|
|
482
|
+
const hasRemoteReplacementHistory = getOpenAiRemoteCompactionPayload(latestCompaction) !== undefined;
|
|
483
|
+
const compactionIndex = latestCompaction ? branchEntries.lastIndexOf(latestCompaction) : -1;
|
|
484
|
+
let anchorIndex = -1;
|
|
485
|
+
for (let index = branchEntries.length - 1; index > compactionIndex; index--) {
|
|
486
|
+
const entry = branchEntries[index];
|
|
487
|
+
if (entry.type !== "message" || entry.message.role !== "assistant") continue;
|
|
488
|
+
const assistant = entry.message;
|
|
489
|
+
if (
|
|
490
|
+
assistant.stopReason !== "aborted" &&
|
|
491
|
+
assistant.stopReason !== "error" &&
|
|
492
|
+
assistant.usage &&
|
|
493
|
+
hasContextTokenUsage(assistant.usage)
|
|
494
|
+
) {
|
|
495
|
+
anchorIndex = index;
|
|
496
|
+
break;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
const entryIndexes = new Map(branchEntries.map((entry, index) => [entry, index]));
|
|
500
|
+
|
|
478
501
|
let toolResultsDropped = 0;
|
|
479
502
|
let blocksDropped = 0;
|
|
480
503
|
let originalTokens = 0;
|
|
481
504
|
let replacementTokens = 0;
|
|
505
|
+
let anchoredTokensRemoved = 0;
|
|
482
506
|
const items = regions.map((region, index) => {
|
|
483
507
|
if (region.kind === "toolResult") toolResultsDropped++;
|
|
484
508
|
else blocksDropped++;
|
|
485
509
|
originalTokens += region.tokens;
|
|
486
510
|
const replacement = replacements[index];
|
|
487
|
-
|
|
511
|
+
const replacementTokenCount = replacement.length > 0 ? countTokens(replacement) : 0;
|
|
512
|
+
replacementTokens += replacementTokenCount;
|
|
513
|
+
const entryIndex = entryIndexes.get(region.entry) ?? -1;
|
|
514
|
+
if (
|
|
515
|
+
entryIndex >= 0 &&
|
|
516
|
+
entryIndex < anchorIndex &&
|
|
517
|
+
(!hasRemoteReplacementHistory || entryIndex > compactionIndex)
|
|
518
|
+
) {
|
|
519
|
+
anchoredTokensRemoved += Math.max(0, region.tokens - replacementTokenCount);
|
|
520
|
+
}
|
|
488
521
|
return { region, replacement };
|
|
489
522
|
});
|
|
490
523
|
|
|
491
524
|
applyShakeRegions(items);
|
|
525
|
+
this.#host.recordAnchoredHistoryRewrite(anchoredTokensRemoved);
|
|
492
526
|
|
|
493
527
|
await this.#host.sessionManager.rewriteEntries();
|
|
494
528
|
const sessionContext = this.#host.buildDisplaySessionContext();
|
|
@@ -1859,7 +1893,7 @@ export class SessionMaintenance {
|
|
|
1859
1893
|
let elideSink = "placeholders";
|
|
1860
1894
|
if (!options.skipElide) {
|
|
1861
1895
|
try {
|
|
1862
|
-
const result = await this.#host.shake("elide", { signal });
|
|
1896
|
+
const result = await this.#host.shake("elide", { config: RESCUE_SHAKE_CONFIG, signal });
|
|
1863
1897
|
elided = result.toolResultsDropped + result.blocksDropped;
|
|
1864
1898
|
elidedTokens = result.tokensFreed;
|
|
1865
1899
|
if (result.artifactId) elideSink = "an artifact";
|
|
@@ -405,17 +405,22 @@ export class SessionPersistenceIndeterminateError extends AggregateError {
|
|
|
405
405
|
* tree by `(id, parentId)`, and the mutable leaf pointer selects which path is
|
|
406
406
|
* active for future appends and for LLM context construction.
|
|
407
407
|
*
|
|
408
|
-
* Durability is software-crash safe but not power-loss safe:
|
|
409
|
-
*
|
|
410
|
-
*
|
|
411
|
-
*
|
|
412
|
-
*
|
|
413
|
-
*
|
|
414
|
-
*
|
|
415
|
-
* that
|
|
416
|
-
*
|
|
417
|
-
*
|
|
418
|
-
*
|
|
408
|
+
* Durability is software-crash safe but not power-loss safe: completed entries
|
|
409
|
+
* (user/assistant/toolResult messages, tool_execution_start markers, custom
|
|
410
|
+
* entries) are handed to the OS synchronously in-body on append and never
|
|
411
|
+
* `fsync`'d. In-flight streaming text is intentionally not durable until
|
|
412
|
+
* `message_end` persists the finished message.
|
|
413
|
+
*
|
|
414
|
+
* While an in-place atomic rewrite is publishing, a concurrent completed append
|
|
415
|
+
* supersedes that publish with a synchronous full-body rewrite so the entry is
|
|
416
|
+
* software-crash durable before the append returns; the abandoned atomic's
|
|
417
|
+
* `commitGuard` then refuses to clobber the fresher body.
|
|
418
|
+
*
|
|
419
|
+
* During {@link moveTo}, appends write a full body to the live relocation path
|
|
420
|
+
* (source until rename, destination once the rename has landed) so a crash mid-
|
|
421
|
+
* move still preserves completed entries without recreating a vacated source.
|
|
422
|
+
* A trailing atomic rewrite still rewrites the header cwd after the path is
|
|
423
|
+
* repointed.
|
|
419
424
|
*/
|
|
420
425
|
export class SessionManager {
|
|
421
426
|
#cwd: string;
|
|
@@ -484,15 +489,12 @@ export class SessionManager {
|
|
|
484
489
|
/** Set by synchronous appends that land while an atomic replacement is active. */
|
|
485
490
|
#atomicRewriteDirty = false;
|
|
486
491
|
/**
|
|
487
|
-
*
|
|
488
|
-
*
|
|
489
|
-
* (
|
|
490
|
-
*
|
|
491
|
-
* otherwise touch the pre-rename path — opening a fresh writer or writing the
|
|
492
|
-
* full body — recreating the very orphan the move eliminates. Both defer
|
|
493
|
-
* while this is set; the move's trailing atomic rewrite persists them.
|
|
492
|
+
* Active {@link moveTo} relocation. Concurrent completed appends write a
|
|
493
|
+
* full body to the live path: source while it still exists, destination
|
|
494
|
+
* once rename has landed (source gone). Never recreates a vacated source.
|
|
495
|
+
* `null` outside an active relocation.
|
|
494
496
|
*/
|
|
495
|
-
#sessionFileRelocating =
|
|
497
|
+
#sessionFileRelocating: { source: string; dest: string } | null = null;
|
|
496
498
|
/** Atomic entry batch currently staged for a full-file commit. */
|
|
497
499
|
#atomicEntryBatch: AtomicEntryBatch | undefined;
|
|
498
500
|
|
|
@@ -763,30 +765,56 @@ export class SessionManager {
|
|
|
763
765
|
return this.#forceFileCreation || this.#fileIsCurrent || this.#historyContainsAssistantMessage();
|
|
764
766
|
}
|
|
765
767
|
|
|
768
|
+
/**
|
|
769
|
+
* Live path for concurrent completed appends during {@link moveTo}.
|
|
770
|
+
* Prefers destination once rename has landed (source gone); otherwise
|
|
771
|
+
* source. Never invents a path that does not already exist.
|
|
772
|
+
*/
|
|
773
|
+
#liveRelocationWritePath(): string | null {
|
|
774
|
+
const relocating = this.#sessionFileRelocating;
|
|
775
|
+
if (!relocating) return null;
|
|
776
|
+
if (this.#storage.existsSync(relocating.dest)) return relocating.dest;
|
|
777
|
+
if (this.#storage.existsSync(relocating.source)) return relocating.source;
|
|
778
|
+
// Rename in flight with neither path visible (rare cross-device edge):
|
|
779
|
+
// fall back to destination so we do not recreate a vacated source.
|
|
780
|
+
return relocating.dest;
|
|
781
|
+
}
|
|
782
|
+
|
|
766
783
|
/**
|
|
767
784
|
* Synchronously rewrite the whole file (header + entries) and keep no open
|
|
768
785
|
* writer; the next append re-opens one. `writeTextSync` returns with the
|
|
769
786
|
* bytes in the kernel page cache, so the file is software-crash durable.
|
|
787
|
+
*
|
|
788
|
+
* During {@link moveTo}, writes to the live relocation path (source pre-
|
|
789
|
+
* rename, destination post-rename) rather than always `#sessionFile`, so
|
|
790
|
+
* concurrent completed entries are durable without recreating a vacated source.
|
|
770
791
|
*/
|
|
771
792
|
#rewriteSynchronously(): void {
|
|
772
|
-
if (!this.#persist || !this.#
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
// orphan the move eliminates) or a path a queued backend publish may clobber
|
|
776
|
-
// post-rename, so defer to moveTo's trailing atomic rewrite. This narrows
|
|
777
|
-
// flushSync durability for entries in the rename window (see class doc).
|
|
778
|
-
if (this.#sessionFileRelocating) return;
|
|
793
|
+
if (!this.#persist || !this.#shouldHaveSessionFile()) return;
|
|
794
|
+
const targetPath = this.#liveRelocationWritePath() ?? this.#sessionFile;
|
|
795
|
+
if (!targetPath) return;
|
|
779
796
|
|
|
780
797
|
try {
|
|
781
798
|
const body = this.#fileBody();
|
|
782
799
|
this.#diskEpoch++;
|
|
783
800
|
this.#diskTail = Promise.resolve();
|
|
784
801
|
this.#closeWriterEventually();
|
|
785
|
-
this.#storage.writeTextSync(
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
this.#
|
|
802
|
+
this.#storage.writeTextSync(targetPath, body);
|
|
803
|
+
// Only mark the manager current when writing the active session path.
|
|
804
|
+
// Mid-move writes update the live relocation path; `#sessionFile` is
|
|
805
|
+
// still the pre-repoint source until moveTo repoints it.
|
|
806
|
+
if (!this.#sessionFileRelocating || targetPath === this.#sessionFile) {
|
|
807
|
+
this.#fileIsCurrent = true;
|
|
808
|
+
this.#materializeBreadcrumb();
|
|
809
|
+
this.#rewriteRequired = false;
|
|
810
|
+
this.#hasTitleSlot = true;
|
|
811
|
+
} else {
|
|
812
|
+
// Destination body is current on disk; in-memory still needs a
|
|
813
|
+
// header-cwd rewrite after repoint, but entries are durable.
|
|
814
|
+
this.#fileIsCurrent = false;
|
|
815
|
+
this.#rewriteRequired = true;
|
|
816
|
+
this.#hasTitleSlot = true;
|
|
817
|
+
}
|
|
790
818
|
} catch (err) {
|
|
791
819
|
this.#noteDiskFailure(err);
|
|
792
820
|
}
|
|
@@ -872,22 +900,21 @@ export class SessionManager {
|
|
|
872
900
|
return;
|
|
873
901
|
}
|
|
874
902
|
|
|
875
|
-
// Atomic replacement window:
|
|
876
|
-
//
|
|
877
|
-
//
|
|
878
|
-
//
|
|
879
|
-
//
|
|
880
|
-
//
|
|
881
|
-
//
|
|
882
|
-
//
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
) {
|
|
888
|
-
this.#fileIsCurrent = false;
|
|
889
|
-
this.#rewriteRequired = true;
|
|
903
|
+
// Atomic replacement / move window: do not open a fresh append writer that
|
|
904
|
+
// a Windows EPERM replace could detach from the current JSONL path.
|
|
905
|
+
// - moveTo: write a full body to the live relocation path (source pre-
|
|
906
|
+
// rename, destination post-rename) so completed entries are durable
|
|
907
|
+
// without recreating a vacated source.
|
|
908
|
+
// - in-place atomic fence: supersede the pending publish with a
|
|
909
|
+
// synchronous full-body rewrite; bumping `#diskEpoch` abandons the
|
|
910
|
+
// in-flight atomic via its `commitGuard`.
|
|
911
|
+
if (this.#sessionFileRelocating) {
|
|
912
|
+
this.#rewriteSynchronously();
|
|
913
|
+
return;
|
|
914
|
+
}
|
|
915
|
+
if (this.#atomicRewriteFenceEpoch !== null && this.#atomicRewriteFenceEpoch === this.#diskEpoch) {
|
|
890
916
|
this.#atomicRewriteDirty = true;
|
|
917
|
+
this.#rewriteSynchronously();
|
|
891
918
|
return;
|
|
892
919
|
}
|
|
893
920
|
// Cold/divergent: not on disk yet, or in-memory entries diverged from the
|
|
@@ -897,15 +924,23 @@ export class SessionManager {
|
|
|
897
924
|
return;
|
|
898
925
|
}
|
|
899
926
|
|
|
900
|
-
// Hot path:
|
|
901
|
-
// chain.
|
|
902
|
-
//
|
|
927
|
+
// Hot path: write the entry directly on the writer, outside the async disk
|
|
928
|
+
// chain. Prefer appendSync so write failures latch `#diskFailure` before
|
|
929
|
+
// this call returns (not via a discarded rejected Promise after a later
|
|
930
|
+
// microtask). Callers stay non-throwing here — the core turn loop invokes
|
|
931
|
+
// appendMessage/appendCustomEntry without try/catch; flushSync/close and
|
|
932
|
+
// subsequent appends still throw the latched error. File writers apply
|
|
933
|
+
// each line to the OS page cache before return.
|
|
903
934
|
// A mid-close writer leaves `#writer` undefined, so `#appendWriter` simply
|
|
904
935
|
// opens a fresh append handle and the entry still lands.
|
|
905
936
|
try {
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
937
|
+
const writer = this.#appendWriter();
|
|
938
|
+
const line = this.#lineFor(entry);
|
|
939
|
+
if (writer.appendSync) {
|
|
940
|
+
writer.appendSync(line);
|
|
941
|
+
} else {
|
|
942
|
+
void writer.append(line).catch(err => this.#noteDiskFailure(err));
|
|
943
|
+
}
|
|
909
944
|
} catch (err) {
|
|
910
945
|
this.#noteDiskFailure(err);
|
|
911
946
|
}
|
|
@@ -921,12 +956,11 @@ export class SessionManager {
|
|
|
921
956
|
}
|
|
922
957
|
|
|
923
958
|
// Title changes use their own asynchronous append path rather than
|
|
924
|
-
// #appendToSessionFile.
|
|
925
|
-
//
|
|
959
|
+
// #appendToSessionFile. During move, write the full body (including the
|
|
960
|
+
// title entry) to the live relocation path so a crash mid-move still
|
|
961
|
+
// keeps the title change; the trailing rewrite still updates header cwd.
|
|
926
962
|
if (this.#sessionFileRelocating) {
|
|
927
|
-
this.#
|
|
928
|
-
this.#rewriteRequired = true;
|
|
929
|
-
this.#atomicRewriteDirty = true;
|
|
963
|
+
this.#rewriteSynchronously();
|
|
930
964
|
return;
|
|
931
965
|
}
|
|
932
966
|
|
|
@@ -1362,14 +1396,15 @@ export class SessionManager {
|
|
|
1362
1396
|
: computeDefaultSessionDir(resolvedCwd, this.#storage));
|
|
1363
1397
|
|
|
1364
1398
|
let sessionFileExisted = false;
|
|
1365
|
-
//
|
|
1366
|
-
//
|
|
1367
|
-
// not a `#diskEpoch` bump
|
|
1368
|
-
//
|
|
1369
|
-
//
|
|
1370
|
-
// below runs it, silently dropping an explicitly materialized session.
|
|
1399
|
+
// Track source+dest for concurrent completed appends during relocation
|
|
1400
|
+
// (see `#sessionFileRelocating`). Existence of either path decides the
|
|
1401
|
+
// live write target — not a `#diskEpoch` bump, which would cancel any
|
|
1402
|
+
// disk task already queued at the current epoch (e.g. a header-only
|
|
1403
|
+
// `ensureOnDisk()` materializing rewrite) before the drain below runs it.
|
|
1371
1404
|
if (this.#persist && this.#sessionFile) {
|
|
1372
|
-
|
|
1405
|
+
const source = this.#sessionFile;
|
|
1406
|
+
const dest = path.join(nextSessionDir, path.basename(source));
|
|
1407
|
+
this.#sessionFileRelocating = { source, dest };
|
|
1373
1408
|
}
|
|
1374
1409
|
|
|
1375
1410
|
try {
|
|
@@ -1433,8 +1468,8 @@ export class SessionManager {
|
|
|
1433
1468
|
this.#sessionFile = newSessionFile;
|
|
1434
1469
|
this.#artifactManager = null;
|
|
1435
1470
|
this.#artifactManagerSessionFile = null;
|
|
1436
|
-
// Path is repointed;
|
|
1437
|
-
this.#sessionFileRelocating =
|
|
1471
|
+
// Path is repointed; hot-path appends may use `#sessionFile` again.
|
|
1472
|
+
this.#sessionFileRelocating = null;
|
|
1438
1473
|
}
|
|
1439
1474
|
|
|
1440
1475
|
this.#cwd = resolvedCwd;
|
|
@@ -1459,7 +1494,7 @@ export class SessionManager {
|
|
|
1459
1494
|
|
|
1460
1495
|
if (this.#sessionFile) this.#rememberBreadcrumb(resolvedCwd, this.#sessionFile);
|
|
1461
1496
|
} finally {
|
|
1462
|
-
this.#sessionFileRelocating =
|
|
1497
|
+
this.#sessionFileRelocating = null;
|
|
1463
1498
|
}
|
|
1464
1499
|
}
|
|
1465
1500
|
|