@markusylisiurunen/tau 0.2.129 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +111 -59
- package/dist/core/async/cli.js +12 -2
- package/dist/core/async/cli.js.map +1 -1
- package/dist/core/async/http_protocol.js +7 -1
- package/dist/core/async/http_protocol.js.map +1 -1
- package/dist/core/async/http_server.js +9 -1
- package/dist/core/async/http_server.js.map +1 -1
- package/dist/core/async/index.js.map +1 -1
- package/dist/core/async/session_manager.js +94 -51
- package/dist/core/async/session_manager.js.map +1 -1
- package/dist/core/cli.js +12 -15
- package/dist/core/cli.js.map +1 -1
- package/dist/core/commands/registry.js +9 -55
- package/dist/core/commands/registry.js.map +1 -1
- package/dist/core/config/index.js.map +1 -1
- package/dist/core/config/runtime.js +0 -1
- package/dist/core/config/runtime.js.map +1 -1
- package/dist/core/config/schema.js +102 -17
- package/dist/core/config/schema.js.map +1 -1
- package/dist/core/debug.js +1 -20
- package/dist/core/debug.js.map +1 -1
- package/dist/core/diff_review/{session.js → bridge.js} +57 -114
- package/dist/core/diff_review/bridge.js.map +1 -0
- package/dist/core/diff_review/index.js +2 -3
- package/dist/core/diff_review/index.js.map +1 -1
- package/dist/core/diff_review/review_thread.js +48 -23
- package/dist/core/diff_review/review_thread.js.map +1 -1
- package/dist/core/diff_review/snapshot.js +40 -11
- package/dist/core/diff_review/snapshot.js.map +1 -1
- package/dist/core/events/parser.js +43 -12
- package/dist/core/events/parser.js.map +1 -1
- package/dist/core/index.js +0 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/modes/index.js +1 -2
- package/dist/core/modes/index.js.map +1 -1
- package/dist/core/modes/rpc_server.js +15 -321
- package/dist/core/modes/rpc_server.js.map +1 -1
- package/dist/core/modes/websocket_server.js +181 -0
- package/dist/core/modes/websocket_server.js.map +1 -0
- package/dist/core/personas.js +11 -1
- package/dist/core/personas.js.map +1 -1
- package/dist/core/runtime/chat_runtime.js +19 -12
- package/dist/core/runtime/chat_runtime.js.map +1 -1
- package/dist/core/runtime/conversation_turn_runtime.js +16 -1
- package/dist/core/runtime/conversation_turn_runtime.js.map +1 -1
- package/dist/core/runtime/runtime_bootstrap.js +326 -0
- package/dist/core/runtime/runtime_bootstrap.js.map +1 -1
- package/dist/core/session/compaction.js +13 -11
- package/dist/core/session/compaction.js.map +1 -1
- package/dist/core/session/core_session.js +9 -0
- package/dist/core/session/core_session.js.map +1 -1
- package/dist/core/session/direct_bash.js +34 -0
- package/dist/core/session/direct_bash.js.map +1 -0
- package/dist/core/session/pruning.js +595 -0
- package/dist/core/session/pruning.js.map +1 -0
- package/dist/core/session/runner.js +45 -5
- package/dist/core/session/runner.js.map +1 -1
- package/dist/core/session/session_engine.js +84 -10
- package/dist/core/session/session_engine.js.map +1 -1
- package/dist/core/static/prompts/diff-review-wrapper.md +1 -1
- package/dist/core/subagents/subagent_engine.js +1 -1
- package/dist/core/subagents/subagent_engine.js.map +1 -1
- package/dist/core/tools/catalog.js.map +1 -1
- package/dist/core/tools/diff_review.js +12 -12
- package/dist/core/tools/diff_review.js.map +1 -1
- package/dist/core/tools/execution_backend.js +9 -2
- package/dist/core/tools/execution_backend.js.map +1 -1
- package/dist/core/tools/registry.js.map +1 -1
- package/dist/core/usage/logs.js.map +1 -1
- package/dist/core/utils/context.js +1 -1
- package/dist/core/utils/context.js.map +1 -1
- package/dist/core/utils/context_builder.js +1 -18
- package/dist/core/utils/context_builder.js.map +1 -1
- package/dist/core/utils/gemini_speech.js +1 -1
- package/dist/core/utils/gemini_speech.js.map +1 -1
- package/dist/core/utils/project_files.js +11 -16
- package/dist/core/utils/project_files.js.map +1 -1
- package/dist/core/utils/spawn_capture.js +2 -2
- package/dist/core/utils/spawn_capture.js.map +1 -1
- package/dist/core/version.js +1 -1
- package/dist/core/version.js.map +1 -1
- package/dist/execution/cloudflare_sandbox_execution_environment.js +400 -0
- package/dist/execution/cloudflare_sandbox_execution_environment.js.map +1 -0
- package/dist/execution/execution_environment.js +24 -0
- package/dist/execution/execution_environment.js.map +1 -0
- package/dist/execution/fly_sprite_execution_environment.js +559 -0
- package/dist/execution/fly_sprite_execution_environment.js.map +1 -0
- package/dist/execution/local_execution_environment.js +93 -0
- package/dist/execution/local_execution_environment.js.map +1 -0
- package/dist/execution/runtime_config_snapshot.js +209 -0
- package/dist/execution/runtime_config_snapshot.js.map +1 -0
- package/dist/execution/sandbox_tool_helpers.js +55 -0
- package/dist/execution/sandbox_tool_helpers.js.map +1 -0
- package/dist/execution/tool_backend_execution_environment.js +55 -0
- package/dist/execution/tool_backend_execution_environment.js.map +1 -0
- package/dist/host/hosted_ephemeral_agent_session.js +286 -0
- package/dist/host/hosted_ephemeral_agent_session.js.map +1 -0
- package/dist/host/index.js +3 -0
- package/dist/host/index.js.map +1 -0
- package/dist/host/local_session_host.js +1464 -0
- package/dist/host/local_session_host.js.map +1 -0
- package/dist/host/session_host.js +2 -0
- package/dist/host/session_host.js.map +1 -0
- package/dist/host/session_protocol_handler.js +873 -0
- package/dist/host/session_protocol_handler.js.map +1 -0
- package/dist/main.js +537 -77
- package/dist/main.js.map +1 -1
- package/dist/protocol/index.d.ts +2 -0
- package/dist/protocol/index.js +2 -0
- package/dist/protocol/index.js.map +1 -0
- package/dist/protocol/session_protocol.d.ts +691 -0
- package/dist/protocol/session_protocol.js +2117 -0
- package/dist/protocol/session_protocol.js.map +1 -0
- package/dist/sdk/client.d.ts +2 -1
- package/dist/sdk/client.js +136 -392
- package/dist/sdk/client.js.map +1 -1
- package/dist/sdk/index.d.ts +7 -4
- package/dist/sdk/index.js +4 -2
- package/dist/sdk/index.js.map +1 -1
- package/dist/sdk/session.d.ts +5 -0
- package/dist/sdk/session.js +406 -0
- package/dist/sdk/session.js.map +1 -0
- package/dist/sdk/types.d.ts +97 -78
- package/dist/store/file_session_store.js +157 -0
- package/dist/store/file_session_store.js.map +1 -0
- package/dist/store/memory_session_store.js +24 -0
- package/dist/store/memory_session_store.js.map +1 -0
- package/dist/store/session_store.js +32 -0
- package/dist/store/session_store.js.map +1 -0
- package/dist/transport/errors.d.ts +29 -0
- package/dist/{sdk → transport}/errors.js +5 -5
- package/dist/transport/errors.js.map +1 -0
- package/dist/transport/in_process_session_transport.js +102 -0
- package/dist/transport/in_process_session_transport.js.map +1 -0
- package/dist/transport/index.d.ts +7 -0
- package/dist/transport/index.js +4 -0
- package/dist/transport/index.js.map +1 -0
- package/dist/transport/pending_session_protocol_requests.js +67 -0
- package/dist/transport/pending_session_protocol_requests.js.map +1 -0
- package/dist/transport/session_protocol_transport_helpers.js +98 -0
- package/dist/transport/session_protocol_transport_helpers.js.map +1 -0
- package/dist/transport/session_transport.d.ts +11 -0
- package/dist/transport/session_transport.js +2 -0
- package/dist/transport/session_transport.js.map +1 -0
- package/dist/transport/stdio_session_transport.d.ts +38 -0
- package/dist/transport/stdio_session_transport.js +243 -0
- package/dist/transport/stdio_session_transport.js.map +1 -0
- package/dist/transport/websocket_session_transport.d.ts +52 -0
- package/dist/transport/websocket_session_transport.js +235 -0
- package/dist/transport/websocket_session_transport.js.map +1 -0
- package/dist/tui/chat_controller/assistant_clipboard.js +39 -0
- package/dist/tui/chat_controller/assistant_clipboard.js.map +1 -0
- package/dist/tui/chat_controller/command_hints.js +43 -0
- package/dist/tui/chat_controller/command_hints.js.map +1 -0
- package/dist/tui/chat_controller/diff_review_service.js +9 -9
- package/dist/tui/chat_controller/diff_review_service.js.map +1 -1
- package/dist/tui/chat_controller/diff_review_user_message.js +17 -0
- package/dist/tui/chat_controller/diff_review_user_message.js.map +1 -0
- package/dist/tui/chat_controller/history_labels.js +8 -0
- package/dist/tui/chat_controller/history_labels.js.map +1 -0
- package/dist/tui/chat_controller/history_message_model.js +36 -0
- package/dist/tui/chat_controller/history_message_model.js.map +1 -0
- package/dist/tui/chat_controller/status_format.js +22 -0
- package/dist/tui/chat_controller/status_format.js.map +1 -0
- package/dist/tui/chat_view.js +0 -1
- package/dist/tui/chat_view.js.map +1 -1
- package/dist/tui/index.js +1 -1
- package/dist/tui/index.js.map +1 -1
- package/dist/tui/listen_capture.js +87 -0
- package/dist/tui/listen_capture.js.map +1 -0
- package/dist/tui/memory_mode.js +23 -0
- package/dist/tui/memory_mode.js.map +1 -0
- package/dist/tui/session_chat_app.js +227 -0
- package/dist/tui/session_chat_app.js.map +1 -0
- package/dist/tui/session_chat_controller.js +2167 -0
- package/dist/tui/session_chat_controller.js.map +1 -0
- package/dist/tui/session_tool_execution_backend.js +170 -0
- package/dist/tui/session_tool_execution_backend.js.map +1 -0
- package/dist/tui/speech_playback.js +107 -0
- package/dist/tui/speech_playback.js.map +1 -0
- package/dist/tui/ui/assistant_message.js +27 -21
- package/dist/tui/ui/assistant_message.js.map +1 -1
- package/dist/tui/ui/custom_editor.js +0 -5
- package/dist/tui/ui/custom_editor.js.map +1 -1
- package/dist/tui/ui/slash_autocomplete.js +14 -36
- package/dist/tui/ui/slash_autocomplete.js.map +1 -1
- package/dist/tui/ui/tool_ui_registry.js +21 -0
- package/dist/tui/ui/tool_ui_registry.js.map +1 -1
- package/package.json +6 -3
- package/dist/core/config/bash_commands.js +0 -44
- package/dist/core/config/bash_commands.js.map +0 -1
- package/dist/core/diff_review/session.js.map +0 -1
- package/dist/core/modes/rpc_adapter.js +0 -17
- package/dist/core/modes/rpc_adapter.js.map +0 -1
- package/dist/core/modes/rpc_protocol.js +0 -500
- package/dist/core/modes/rpc_protocol.js.map +0 -1
- package/dist/core/session/checkpoint.js +0 -34
- package/dist/core/session/checkpoint.js.map +0 -1
- package/dist/sdk/errors.d.ts +0 -33
- package/dist/sdk/errors.js.map +0 -1
- package/dist/tui/app.js +0 -64
- package/dist/tui/app.js.map +0 -1
- package/dist/tui/chat_controller/session_maintenance_service.js +0 -634
- package/dist/tui/chat_controller/session_maintenance_service.js.map +0 -1
- package/dist/tui/chat_controller.js +0 -2579
- package/dist/tui/chat_controller.js.map +0 -1
package/dist/tui/app.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"app.js","sourceRoot":"","sources":["../../src/tui/app.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,634 +0,0 @@
|
|
|
1
|
-
import { TOOL_NAME_BASH, TOOL_NAME_EDIT } from "../../core/tools/tool_names.js";
|
|
2
|
-
import { buildLineDiff, collapseLongUnchangedDiffRuns } from "../../core/utils/line_diff.js";
|
|
3
|
-
import { bytesToTokens, formatTokenEstimate, tokensToBytes } from "../../core/utils/token.js";
|
|
4
|
-
import { truncateToBytesFromStart } from "../../core/utils/truncate.js";
|
|
5
|
-
const DEFAULT_PRUNE_FRACTION = 0.25;
|
|
6
|
-
const PRUNED_TOOL_RESULT_PREFIX = "[Tool result pruned]";
|
|
7
|
-
const PRUNED_EDIT_RESULT_PREFIX = "[Tool result pruned] Edit diff";
|
|
8
|
-
const PRUNED_EDIT_ARGUMENT_MARKER = "[Content pruned]";
|
|
9
|
-
const PRUNE_EDIT_UNCHANGED_CONTEXT_LINES = 4;
|
|
10
|
-
const PRUNE_PREVIEW_MAX_TOKENS = 512;
|
|
11
|
-
const PRUNE_MAX_OVERAGE_RATIO = 0.1;
|
|
12
|
-
export class SessionMaintenanceService {
|
|
13
|
-
engine;
|
|
14
|
-
view;
|
|
15
|
-
runStreamingTask;
|
|
16
|
-
applyCompactedHistoryUi;
|
|
17
|
-
requestSmartPruneSelection;
|
|
18
|
-
constructor(options) {
|
|
19
|
-
this.engine = options.engine;
|
|
20
|
-
this.view = options.view;
|
|
21
|
-
this.runStreamingTask = options.runStreamingTask;
|
|
22
|
-
this.applyCompactedHistoryUi = options.applyCompactedHistoryUi;
|
|
23
|
-
this.requestSmartPruneSelection = options.requestSmartPruneSelection;
|
|
24
|
-
}
|
|
25
|
-
async compactSummaryOnly(guidance) {
|
|
26
|
-
this.view.addSystemMessage("summarizing session...", "success");
|
|
27
|
-
let handledFailure = false;
|
|
28
|
-
const outcome = await this.runStreamingTask(async (signal) => {
|
|
29
|
-
const result = await this.engine.compact({
|
|
30
|
-
mode: "only-summary",
|
|
31
|
-
guidance,
|
|
32
|
-
signal,
|
|
33
|
-
});
|
|
34
|
-
this.applyCompactedHistoryUi(result.compactionMessage);
|
|
35
|
-
this.view.addSystemMessage("session compacted. previous context has been summarized.", "success");
|
|
36
|
-
}, (taskOutcome) => {
|
|
37
|
-
if (!taskOutcome.error || taskOutcome.aborted) {
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
handledFailure = true;
|
|
41
|
-
this.handleCompactionError(taskOutcome.error);
|
|
42
|
-
});
|
|
43
|
-
if (!handledFailure && outcome.error && !outcome.aborted) {
|
|
44
|
-
this.handleCompactionError(outcome.error);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
async compactSummaryAndLast(guidance) {
|
|
48
|
-
this.view.addSystemMessage("summarizing session...", "success");
|
|
49
|
-
let handledFailure = false;
|
|
50
|
-
const outcome = await this.runStreamingTask(async (signal) => {
|
|
51
|
-
const result = await this.engine.compact({
|
|
52
|
-
mode: "with-last-assistant",
|
|
53
|
-
guidance,
|
|
54
|
-
signal,
|
|
55
|
-
});
|
|
56
|
-
this.applyCompactedHistoryUi(result.compactionMessage);
|
|
57
|
-
const successText = result.includedLastAssistant
|
|
58
|
-
? "session compacted. previous context and last assistant message have been included."
|
|
59
|
-
: "session compacted. previous context has been summarized.";
|
|
60
|
-
this.view.addSystemMessage(successText, "success");
|
|
61
|
-
}, (taskOutcome) => {
|
|
62
|
-
if (!taskOutcome.error || taskOutcome.aborted) {
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
handledFailure = true;
|
|
66
|
-
this.handleCompactionError(taskOutcome.error);
|
|
67
|
-
});
|
|
68
|
-
if (!handledFailure && outcome.error && !outcome.aborted) {
|
|
69
|
-
this.handleCompactionError(outcome.error);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
pruneToolResults(strategy, extra) {
|
|
73
|
-
const fraction = this.parsePruneFraction(extra);
|
|
74
|
-
if (fraction === null) {
|
|
75
|
-
this.view.addSystemMessage("invalid prune fraction. use a number between 0 and 1.", "error");
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
if (fraction === 0) {
|
|
79
|
-
this.view.addSystemMessage("prune fraction is 0, nothing to prune.", "warn");
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
const historyEntries = this.engine.historyEntries;
|
|
83
|
-
if (historyEntries.length === 0) {
|
|
84
|
-
this.view.addSystemMessage("no conversation to prune.", "warn");
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
let editSummary;
|
|
88
|
-
try {
|
|
89
|
-
editSummary = this.pruneEditToolHistory(historyEntries);
|
|
90
|
-
}
|
|
91
|
-
catch (error) {
|
|
92
|
-
this.view.addSystemMessage(`prune failed: ${error.message}`, "error");
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
const prune = this.preparePruneCandidates(historyEntries, fraction);
|
|
96
|
-
if (prune.candidates.length === 0 || prune.totalTokens === 0) {
|
|
97
|
-
this.reportPruneNoop(editSummary, "no bash tool results or edit tool calls to prune.");
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
if (prune.targetTokens <= 0) {
|
|
101
|
-
this.reportPruneNoop(editSummary, "prune fraction is too small to remove anything.");
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
const toPrune = this.selectPruneCandidatesByStrategy(prune, strategy);
|
|
105
|
-
if (toPrune.length === 0) {
|
|
106
|
-
this.reportPruneNoop(editSummary, "no bash tool results or edit tool calls to prune.");
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
const applied = this.applyBashPruneCandidates(toPrune);
|
|
110
|
-
this.view.addSystemMessage(this.buildPruneSummaryMessage(editSummary, applied.resultsPruned, applied.bytesPruned), "success");
|
|
111
|
-
}
|
|
112
|
-
async pruneToolResultsSmart(extra) {
|
|
113
|
-
const parsed = this.parsePruneFractionAndGuidance(extra);
|
|
114
|
-
if (parsed.fraction === null) {
|
|
115
|
-
this.view.addSystemMessage("invalid prune fraction. use a number between 0 and 1.", "error");
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
const fraction = parsed.fraction;
|
|
119
|
-
if (fraction === 0) {
|
|
120
|
-
this.view.addSystemMessage("prune fraction is 0, nothing to prune.", "warn");
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
const historyEntries = this.engine.historyEntries;
|
|
124
|
-
if (historyEntries.length === 0) {
|
|
125
|
-
this.view.addSystemMessage("no conversation to prune.", "warn");
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
try {
|
|
129
|
-
const prune = this.preparePruneCandidates(historyEntries, fraction);
|
|
130
|
-
let editSummary;
|
|
131
|
-
const getEditSummary = () => {
|
|
132
|
-
if (!editSummary) {
|
|
133
|
-
editSummary = this.pruneEditToolHistory(historyEntries);
|
|
134
|
-
}
|
|
135
|
-
return editSummary;
|
|
136
|
-
};
|
|
137
|
-
if (prune.candidates.length === 0 || prune.totalTokens === 0) {
|
|
138
|
-
this.reportPruneNoop(getEditSummary(), "no bash tool results or edit tool calls to prune.");
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
if (prune.targetTokens <= 0) {
|
|
142
|
-
this.reportPruneNoop(getEditSummary(), "prune fraction is too small to remove anything.");
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
this.view.addSystemMessage("sampling prune candidates...", "success");
|
|
146
|
-
let handledFailure = false;
|
|
147
|
-
const selectionOutcome = await this.runStreamingTask(async (signal) => {
|
|
148
|
-
const prompt = this.buildSmartPrunePrompt({
|
|
149
|
-
history: historyEntries.map((entry) => entry.message),
|
|
150
|
-
targetTokens: prune.targetTokens,
|
|
151
|
-
guidance: parsed.guidance,
|
|
152
|
-
});
|
|
153
|
-
const selection = await this.requestSmartPruneSelection(prompt, signal);
|
|
154
|
-
if (selection.length === 0) {
|
|
155
|
-
this.reportPruneNoop(getEditSummary(), "model returned no prune candidates.");
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
const toPrune = this.selectSmartPruneCandidates(selection, prune.candidates, prune.targetTokens);
|
|
159
|
-
if (toPrune.length === 0) {
|
|
160
|
-
this.reportPruneNoop(getEditSummary(), "no bash tool results or edit tool calls to prune.");
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
const applied = this.applyBashPruneCandidates(toPrune);
|
|
164
|
-
this.view.addSystemMessage(this.buildPruneSummaryMessage(getEditSummary(), applied.resultsPruned, applied.bytesPruned), "success");
|
|
165
|
-
}, (taskOutcome) => {
|
|
166
|
-
if (!taskOutcome.error || taskOutcome.aborted) {
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
handledFailure = true;
|
|
170
|
-
this.view.addSystemMessage(`prune failed: ${taskOutcome.error.message}`, "error");
|
|
171
|
-
});
|
|
172
|
-
if (!handledFailure && selectionOutcome.error && !selectionOutcome.aborted) {
|
|
173
|
-
this.view.addSystemMessage(`prune failed: ${selectionOutcome.error.message}`, "error");
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
catch (error) {
|
|
177
|
-
this.view.addSystemMessage(`prune failed: ${error.message}`, "error");
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
handleCompactionError(err) {
|
|
181
|
-
const message = err.message || "compaction failed";
|
|
182
|
-
if (message === "no conversation to compact.") {
|
|
183
|
-
this.view.addSystemMessage(message, "warn");
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
this.view.addSystemMessage(`compact failed: ${message}`, "error");
|
|
187
|
-
}
|
|
188
|
-
parsePruneFraction(extra) {
|
|
189
|
-
if (!extra) {
|
|
190
|
-
return DEFAULT_PRUNE_FRACTION;
|
|
191
|
-
}
|
|
192
|
-
const parsed = Number(extra);
|
|
193
|
-
if (!Number.isFinite(parsed)) {
|
|
194
|
-
return null;
|
|
195
|
-
}
|
|
196
|
-
if (parsed < 0 || parsed > 1) {
|
|
197
|
-
return null;
|
|
198
|
-
}
|
|
199
|
-
return parsed;
|
|
200
|
-
}
|
|
201
|
-
parsePruneFractionAndGuidance(extra) {
|
|
202
|
-
if (!extra) {
|
|
203
|
-
return { fraction: DEFAULT_PRUNE_FRACTION };
|
|
204
|
-
}
|
|
205
|
-
const trimmed = extra.trim();
|
|
206
|
-
if (!trimmed) {
|
|
207
|
-
return { fraction: DEFAULT_PRUNE_FRACTION };
|
|
208
|
-
}
|
|
209
|
-
const [firstToken, ...rest] = trimmed.split(/\s+/);
|
|
210
|
-
const parsed = Number(firstToken);
|
|
211
|
-
if (Number.isFinite(parsed)) {
|
|
212
|
-
if (parsed < 0 || parsed > 1) {
|
|
213
|
-
return { fraction: null };
|
|
214
|
-
}
|
|
215
|
-
const guidance = rest.join(" ").trim();
|
|
216
|
-
return guidance ? { fraction: parsed, guidance } : { fraction: parsed };
|
|
217
|
-
}
|
|
218
|
-
return { fraction: DEFAULT_PRUNE_FRACTION, guidance: trimmed };
|
|
219
|
-
}
|
|
220
|
-
preparePruneCandidates(historyEntries, fraction) {
|
|
221
|
-
const candidates = [];
|
|
222
|
-
let totalTokens = 0;
|
|
223
|
-
for (const entry of historyEntries) {
|
|
224
|
-
const message = entry.message;
|
|
225
|
-
if (message.role !== "toolResult") {
|
|
226
|
-
continue;
|
|
227
|
-
}
|
|
228
|
-
const toolResult = message;
|
|
229
|
-
if (toolResult.toolName !== TOOL_NAME_BASH) {
|
|
230
|
-
continue;
|
|
231
|
-
}
|
|
232
|
-
const inspection = this.inspectToolResultContent(toolResult);
|
|
233
|
-
if (inspection.firstText?.startsWith(PRUNED_TOOL_RESULT_PREFIX) || inspection.hasImage) {
|
|
234
|
-
continue;
|
|
235
|
-
}
|
|
236
|
-
const tokens = bytesToTokens(inspection.textBytes);
|
|
237
|
-
candidates.push({
|
|
238
|
-
historyEntryId: entry.id,
|
|
239
|
-
toolResult,
|
|
240
|
-
bytes: inspection.textBytes,
|
|
241
|
-
tokens,
|
|
242
|
-
order: candidates.length,
|
|
243
|
-
});
|
|
244
|
-
totalTokens += tokens;
|
|
245
|
-
}
|
|
246
|
-
return {
|
|
247
|
-
candidates,
|
|
248
|
-
totalTokens,
|
|
249
|
-
targetTokens: Math.ceil(totalTokens * fraction),
|
|
250
|
-
};
|
|
251
|
-
}
|
|
252
|
-
selectPruneCandidatesByStrategy(prune, strategy) {
|
|
253
|
-
const ordered = strategy === "largest"
|
|
254
|
-
? [...prune.candidates].sort((a, b) => b.tokens - a.tokens || b.bytes - a.bytes || a.order - b.order)
|
|
255
|
-
: prune.candidates;
|
|
256
|
-
const selected = [];
|
|
257
|
-
let selectedTokens = 0;
|
|
258
|
-
for (const candidate of ordered) {
|
|
259
|
-
if (selectedTokens >= prune.targetTokens) {
|
|
260
|
-
break;
|
|
261
|
-
}
|
|
262
|
-
selected.push(candidate);
|
|
263
|
-
selectedTokens += candidate.tokens;
|
|
264
|
-
}
|
|
265
|
-
return selected;
|
|
266
|
-
}
|
|
267
|
-
applyBashPruneCandidates(candidates) {
|
|
268
|
-
let bytesPruned = 0;
|
|
269
|
-
for (const candidate of candidates) {
|
|
270
|
-
bytesPruned += candidate.bytes;
|
|
271
|
-
const noticeText = this.buildPrunedToolResultNotice(candidate.toolResult, candidate.bytes);
|
|
272
|
-
const prunedResult = {
|
|
273
|
-
...candidate.toolResult,
|
|
274
|
-
content: [{ type: "text", text: noticeText }],
|
|
275
|
-
};
|
|
276
|
-
this.engine.replaceMessageById(candidate.historyEntryId, prunedResult);
|
|
277
|
-
this.emitToolResultPrunedUiEvent(prunedResult.toolCallId, noticeText);
|
|
278
|
-
}
|
|
279
|
-
return { resultsPruned: candidates.length, bytesPruned };
|
|
280
|
-
}
|
|
281
|
-
reportPruneNoop(editSummary, fallbackMessage) {
|
|
282
|
-
if (this.hasAnyPrunedEditChanges(editSummary)) {
|
|
283
|
-
this.view.addSystemMessage(this.buildPruneSummaryMessage(editSummary), "success");
|
|
284
|
-
return;
|
|
285
|
-
}
|
|
286
|
-
this.view.addSystemMessage(fallbackMessage, "warn");
|
|
287
|
-
}
|
|
288
|
-
hasAnyPrunedEditChanges(summary) {
|
|
289
|
-
return summary.callsPruned > 0 || summary.resultsPruned > 0;
|
|
290
|
-
}
|
|
291
|
-
buildPruneSummaryMessage(editSummary, bashResultsPruned = 0, bashBytesPruned = 0) {
|
|
292
|
-
const editSummaryText = this.formatPrunedEditSummary(editSummary);
|
|
293
|
-
if (bashResultsPruned <= 0) {
|
|
294
|
-
if (!editSummaryText) {
|
|
295
|
-
return "no bash tool results or edit tool calls to prune.";
|
|
296
|
-
}
|
|
297
|
-
return `pruned ${editSummaryText}.`;
|
|
298
|
-
}
|
|
299
|
-
const bashLabel = formatTokenEstimate(bashBytesPruned);
|
|
300
|
-
const bashNoun = bashResultsPruned === 1 ? "result" : "results";
|
|
301
|
-
if (!editSummaryText) {
|
|
302
|
-
return `pruned ${bashResultsPruned} bash tool ${bashNoun} (${bashLabel}).`;
|
|
303
|
-
}
|
|
304
|
-
return `pruned ${bashResultsPruned} bash tool ${bashNoun} (${bashLabel}) and ${editSummaryText}.`;
|
|
305
|
-
}
|
|
306
|
-
formatPrunedEditSummary(summary) {
|
|
307
|
-
if (!this.hasAnyPrunedEditChanges(summary)) {
|
|
308
|
-
return "";
|
|
309
|
-
}
|
|
310
|
-
const parts = [];
|
|
311
|
-
if (summary.callsPruned > 0) {
|
|
312
|
-
const noun = summary.callsPruned === 1 ? "call" : "calls";
|
|
313
|
-
parts.push(`${summary.callsPruned} edit tool ${noun}`);
|
|
314
|
-
}
|
|
315
|
-
if (summary.resultsPruned > 0) {
|
|
316
|
-
const noun = summary.resultsPruned === 1 ? "result" : "results";
|
|
317
|
-
parts.push(`${summary.resultsPruned} edit tool ${noun}`);
|
|
318
|
-
}
|
|
319
|
-
const tokenEstimate = formatTokenEstimate(summary.bytesRemoved);
|
|
320
|
-
return `${parts.join(" and ")} (${tokenEstimate})`;
|
|
321
|
-
}
|
|
322
|
-
pruneEditToolHistory(historyEntries) {
|
|
323
|
-
const summary = {
|
|
324
|
-
callsPruned: 0,
|
|
325
|
-
resultsPruned: 0,
|
|
326
|
-
bytesRemoved: 0,
|
|
327
|
-
};
|
|
328
|
-
const editCallsById = new Map();
|
|
329
|
-
for (const entry of historyEntries) {
|
|
330
|
-
const message = entry.message;
|
|
331
|
-
if (message.role !== "assistant") {
|
|
332
|
-
continue;
|
|
333
|
-
}
|
|
334
|
-
const assistant = message;
|
|
335
|
-
const assistantContent = this.getAssistantContentOrThrow(assistant, "pruning edit tool calls");
|
|
336
|
-
let changed = false;
|
|
337
|
-
const nextContent = assistantContent.map((block) => {
|
|
338
|
-
if (block.type !== "toolCall") {
|
|
339
|
-
return block;
|
|
340
|
-
}
|
|
341
|
-
const toolCall = block;
|
|
342
|
-
if (toolCall.name !== TOOL_NAME_EDIT) {
|
|
343
|
-
return block;
|
|
344
|
-
}
|
|
345
|
-
const args = this.getToolCallArgumentsObject(toolCall);
|
|
346
|
-
const oldText = this.getRequiredEditArgument(args, "oldText", toolCall.id);
|
|
347
|
-
const newText = this.getRequiredEditArgument(args, "newText", toolCall.id);
|
|
348
|
-
if (oldText === PRUNED_EDIT_ARGUMENT_MARKER && newText === PRUNED_EDIT_ARGUMENT_MARKER) {
|
|
349
|
-
return block;
|
|
350
|
-
}
|
|
351
|
-
changed = true;
|
|
352
|
-
summary.callsPruned += 1;
|
|
353
|
-
const oldBytes = Buffer.byteLength(oldText, "utf8");
|
|
354
|
-
const newBytes = Buffer.byteLength(newText, "utf8");
|
|
355
|
-
const markerBytes = Buffer.byteLength(PRUNED_EDIT_ARGUMENT_MARKER, "utf8") * 2;
|
|
356
|
-
summary.bytesRemoved += Math.max(0, oldBytes + newBytes - markerBytes);
|
|
357
|
-
editCallsById.set(toolCall.id, { oldText, newText });
|
|
358
|
-
return {
|
|
359
|
-
...toolCall,
|
|
360
|
-
arguments: {
|
|
361
|
-
...args,
|
|
362
|
-
oldText: PRUNED_EDIT_ARGUMENT_MARKER,
|
|
363
|
-
newText: PRUNED_EDIT_ARGUMENT_MARKER,
|
|
364
|
-
},
|
|
365
|
-
};
|
|
366
|
-
});
|
|
367
|
-
if (changed) {
|
|
368
|
-
this.engine.replaceMessageById(entry.id, { ...assistant, content: nextContent });
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
if (editCallsById.size === 0) {
|
|
372
|
-
return summary;
|
|
373
|
-
}
|
|
374
|
-
for (const entry of historyEntries) {
|
|
375
|
-
const message = entry.message;
|
|
376
|
-
if (message.role !== "toolResult") {
|
|
377
|
-
continue;
|
|
378
|
-
}
|
|
379
|
-
const toolResult = message;
|
|
380
|
-
if (toolResult.toolName !== TOOL_NAME_EDIT) {
|
|
381
|
-
continue;
|
|
382
|
-
}
|
|
383
|
-
if (toolResult.isError) {
|
|
384
|
-
continue;
|
|
385
|
-
}
|
|
386
|
-
const callDiff = editCallsById.get(toolResult.toolCallId);
|
|
387
|
-
if (!callDiff) {
|
|
388
|
-
continue;
|
|
389
|
-
}
|
|
390
|
-
const inspection = this.inspectToolResultContent(toolResult);
|
|
391
|
-
if (inspection.firstText?.startsWith(PRUNED_EDIT_RESULT_PREFIX)) {
|
|
392
|
-
continue;
|
|
393
|
-
}
|
|
394
|
-
const prunedText = this.buildPrunedEditToolResult(callDiff);
|
|
395
|
-
const prunedResult = {
|
|
396
|
-
...toolResult,
|
|
397
|
-
content: [{ type: "text", text: prunedText }],
|
|
398
|
-
};
|
|
399
|
-
this.engine.replaceMessageById(entry.id, prunedResult);
|
|
400
|
-
this.emitToolResultPrunedUiEvent(prunedResult.toolCallId, prunedText);
|
|
401
|
-
summary.resultsPruned += 1;
|
|
402
|
-
}
|
|
403
|
-
return summary;
|
|
404
|
-
}
|
|
405
|
-
getAssistantContentOrThrow(assistant, context) {
|
|
406
|
-
if (!Array.isArray(assistant.content)) {
|
|
407
|
-
throw new Error(`invalid assistant message content while ${context}`);
|
|
408
|
-
}
|
|
409
|
-
return assistant.content;
|
|
410
|
-
}
|
|
411
|
-
getToolCallArgumentsObject(toolCall) {
|
|
412
|
-
const { arguments: argumentsValue } = toolCall;
|
|
413
|
-
if (!argumentsValue || typeof argumentsValue !== "object" || Array.isArray(argumentsValue)) {
|
|
414
|
-
throw new Error(`invalid edit tool call arguments (tool_call_id=${toolCall.id})`);
|
|
415
|
-
}
|
|
416
|
-
return argumentsValue;
|
|
417
|
-
}
|
|
418
|
-
getRequiredEditArgument(args, key, toolCallId) {
|
|
419
|
-
const value = args[key];
|
|
420
|
-
if (typeof value !== "string") {
|
|
421
|
-
throw new Error(`missing edit argument '${key}' (tool_call_id=${toolCallId})`);
|
|
422
|
-
}
|
|
423
|
-
return value;
|
|
424
|
-
}
|
|
425
|
-
buildPrunedEditToolResult(diff) {
|
|
426
|
-
const diffText = this.buildPrunedEditDiff(diff.oldText, diff.newText);
|
|
427
|
-
return `${PRUNED_EDIT_RESULT_PREFIX}\n${diffText}`;
|
|
428
|
-
}
|
|
429
|
-
buildPrunedEditDiff(oldText, newText) {
|
|
430
|
-
const diff = buildLineDiff(oldText, newText);
|
|
431
|
-
if (diff.added === 0 && diff.removed === 0) {
|
|
432
|
-
return "(No textual changes)";
|
|
433
|
-
}
|
|
434
|
-
const collapsed = collapseLongUnchangedDiffRuns({
|
|
435
|
-
diffLines: diff.lines,
|
|
436
|
-
maxUnchangedLines: PRUNE_EDIT_UNCHANGED_CONTEXT_LINES,
|
|
437
|
-
});
|
|
438
|
-
return collapsed.length > 0 ? collapsed.join("\n") : "(No textual changes)";
|
|
439
|
-
}
|
|
440
|
-
buildPrunedToolResultNotice(toolResult, bytes) {
|
|
441
|
-
const tokenEstimate = formatTokenEstimate(bytes);
|
|
442
|
-
return `${PRUNED_TOOL_RESULT_PREFIX} ${toolResult.toolName} output removed (${tokenEstimate}). Re-run the command if needed.`;
|
|
443
|
-
}
|
|
444
|
-
emitToolResultPrunedUiEvent(toolCallId, content) {
|
|
445
|
-
this.view.handleToolUiEvent({
|
|
446
|
-
type: "tool_pruned",
|
|
447
|
-
toolCallId,
|
|
448
|
-
content,
|
|
449
|
-
});
|
|
450
|
-
}
|
|
451
|
-
inspectToolResultContent(toolResult) {
|
|
452
|
-
const parts = [];
|
|
453
|
-
let textBytes = 0;
|
|
454
|
-
let hasImage = false;
|
|
455
|
-
let firstText;
|
|
456
|
-
for (const block of toolResult.content) {
|
|
457
|
-
if (block.type === "text") {
|
|
458
|
-
if (firstText === undefined) {
|
|
459
|
-
firstText = block.text;
|
|
460
|
-
}
|
|
461
|
-
parts.push(block.text);
|
|
462
|
-
textBytes += Buffer.byteLength(block.text, "utf8");
|
|
463
|
-
continue;
|
|
464
|
-
}
|
|
465
|
-
if (block.type === "image") {
|
|
466
|
-
hasImage = true;
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
return {
|
|
470
|
-
text: parts.join("\n").trimEnd(),
|
|
471
|
-
textBytes,
|
|
472
|
-
hasImage,
|
|
473
|
-
firstText,
|
|
474
|
-
};
|
|
475
|
-
}
|
|
476
|
-
buildSmartPrunePrompt(args) {
|
|
477
|
-
const lines = [];
|
|
478
|
-
lines.push(`Target pruning token budget: ${args.targetTokens}`);
|
|
479
|
-
lines.push([
|
|
480
|
-
"The budget is the total tokens to prune.",
|
|
481
|
-
"It is better to slightly exceed it than to be significantly below it.",
|
|
482
|
-
].join(" "));
|
|
483
|
-
lines.push('Return only JSON: {"prune":[...]} with tool_call_id values from the conversation history.');
|
|
484
|
-
lines.push("Only include tool_call_id values for bash tool results.");
|
|
485
|
-
if (args.guidance?.trim()) {
|
|
486
|
-
lines.push(`Guidance (prioritize this): ${args.guidance.trim()}`);
|
|
487
|
-
}
|
|
488
|
-
else {
|
|
489
|
-
lines.push("Guidance: (none provided, use your best judgment)");
|
|
490
|
-
}
|
|
491
|
-
lines.push("");
|
|
492
|
-
lines.push("Conversation history:");
|
|
493
|
-
lines.push("<conversation>");
|
|
494
|
-
lines.push(...this.formatPruneConversationHistory(args.history));
|
|
495
|
-
lines.push("</conversation>");
|
|
496
|
-
return lines.join("\n");
|
|
497
|
-
}
|
|
498
|
-
formatPruneConversationHistory(history) {
|
|
499
|
-
const lines = [];
|
|
500
|
-
for (const message of history) {
|
|
501
|
-
if (message.role === "user") {
|
|
502
|
-
lines.push("<user>");
|
|
503
|
-
this.appendPruneContentLines(lines, message.content);
|
|
504
|
-
lines.push("</user>");
|
|
505
|
-
lines.push("");
|
|
506
|
-
continue;
|
|
507
|
-
}
|
|
508
|
-
if (message.role === "assistant") {
|
|
509
|
-
lines.push("<assistant>");
|
|
510
|
-
const assistant = message;
|
|
511
|
-
const assistantContent = this.getAssistantContentOrThrow(assistant, "building smart prune prompt");
|
|
512
|
-
for (const block of assistantContent) {
|
|
513
|
-
if (block.type === "text") {
|
|
514
|
-
this.appendPruneText(lines, block.text);
|
|
515
|
-
continue;
|
|
516
|
-
}
|
|
517
|
-
if (block.type === "toolCall") {
|
|
518
|
-
const toolCall = block;
|
|
519
|
-
lines.push(this.buildPruneToolCallTag(toolCall));
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
lines.push("</assistant>");
|
|
523
|
-
lines.push("");
|
|
524
|
-
continue;
|
|
525
|
-
}
|
|
526
|
-
if (message.role === "toolResult") {
|
|
527
|
-
const toolResult = message;
|
|
528
|
-
lines.push(...this.buildPruneToolResultLines(toolResult));
|
|
529
|
-
lines.push("");
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
if (lines[lines.length - 1] === "") {
|
|
533
|
-
lines.pop();
|
|
534
|
-
}
|
|
535
|
-
return lines;
|
|
536
|
-
}
|
|
537
|
-
appendPruneContentLines(lines, content) {
|
|
538
|
-
if (typeof content === "string") {
|
|
539
|
-
this.appendPruneText(lines, content);
|
|
540
|
-
return;
|
|
541
|
-
}
|
|
542
|
-
if (!Array.isArray(content)) {
|
|
543
|
-
return;
|
|
544
|
-
}
|
|
545
|
-
for (const block of content) {
|
|
546
|
-
if (block.type === "text") {
|
|
547
|
-
this.appendPruneText(lines, block.text);
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
appendPruneText(lines, text) {
|
|
552
|
-
const escaped = this.escapeXml(text);
|
|
553
|
-
const parts = escaped.split(/\r?\n/);
|
|
554
|
-
for (const part of parts) {
|
|
555
|
-
lines.push(part);
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
buildPruneToolCallTag(toolCall) {
|
|
559
|
-
const name = this.escapeXmlAttribute(toolCall.name);
|
|
560
|
-
const id = this.escapeXmlAttribute(toolCall.id);
|
|
561
|
-
const argsValue = JSON.stringify(toolCall.arguments);
|
|
562
|
-
const args = this.escapeXmlAttribute(argsValue);
|
|
563
|
-
return `<tool-call name="${name}" id="${id}" args="${args}" />`;
|
|
564
|
-
}
|
|
565
|
-
buildPruneToolResultLines(toolResult) {
|
|
566
|
-
const lines = [];
|
|
567
|
-
const name = this.escapeXmlAttribute(toolResult.toolName);
|
|
568
|
-
const toolCallId = this.escapeXmlAttribute(toolResult.toolCallId);
|
|
569
|
-
const inspection = this.inspectToolResultContent(toolResult);
|
|
570
|
-
if (inspection.hasImage) {
|
|
571
|
-
lines.push(`<tool-result name="${name}" tool_call_id="${toolCallId}">`);
|
|
572
|
-
lines.push("<preview>[image omitted]</preview>");
|
|
573
|
-
lines.push("</tool-result>");
|
|
574
|
-
return lines;
|
|
575
|
-
}
|
|
576
|
-
const preview = this.buildPruneToolResultPreview(inspection.text);
|
|
577
|
-
lines.push(`<tool-result name="${name}" tool_call_id="${toolCallId}" total_tokens="${preview.totalTokens}">`);
|
|
578
|
-
lines.push(`<preview max_tokens="${PRUNE_PREVIEW_MAX_TOKENS}">`);
|
|
579
|
-
lines.push(...preview.lines);
|
|
580
|
-
lines.push("</preview>");
|
|
581
|
-
lines.push("</tool-result>");
|
|
582
|
-
return lines;
|
|
583
|
-
}
|
|
584
|
-
buildPruneToolResultPreview(text) {
|
|
585
|
-
const normalized = text.trimEnd();
|
|
586
|
-
const totalBytes = Buffer.byteLength(normalized, "utf8");
|
|
587
|
-
const totalTokens = bytesToTokens(totalBytes);
|
|
588
|
-
if (totalTokens <= PRUNE_PREVIEW_MAX_TOKENS) {
|
|
589
|
-
const escaped = this.escapeXml(normalized);
|
|
590
|
-
const lines = escaped ? escaped.split(/\r?\n/) : [""];
|
|
591
|
-
return { lines, totalTokens };
|
|
592
|
-
}
|
|
593
|
-
const head = truncateToBytesFromStart(normalized, tokensToBytes(PRUNE_PREVIEW_MAX_TOKENS));
|
|
594
|
-
const remainingTokens = Math.max(0, totalTokens - PRUNE_PREVIEW_MAX_TOKENS);
|
|
595
|
-
const suffix = `…${remainingTokens} more tokens…`;
|
|
596
|
-
const previewText = head.endsWith("\n") || head === "" ? `${head}${suffix}` : `${head}\n${suffix}`;
|
|
597
|
-
const escaped = this.escapeXml(previewText);
|
|
598
|
-
const lines = escaped ? escaped.split(/\r?\n/) : [""];
|
|
599
|
-
return { lines, totalTokens };
|
|
600
|
-
}
|
|
601
|
-
selectSmartPruneCandidates(ids, candidates, targetTokens) {
|
|
602
|
-
const candidatesById = new Map();
|
|
603
|
-
for (const candidate of candidates) {
|
|
604
|
-
candidatesById.set(candidate.toolResult.toolCallId, candidate);
|
|
605
|
-
}
|
|
606
|
-
const selected = [];
|
|
607
|
-
const seen = new Set();
|
|
608
|
-
let tokens = 0;
|
|
609
|
-
const maxTokens = Math.ceil(targetTokens * (1 + PRUNE_MAX_OVERAGE_RATIO));
|
|
610
|
-
for (const id of ids) {
|
|
611
|
-
if (seen.has(id))
|
|
612
|
-
continue;
|
|
613
|
-
const candidate = candidatesById.get(id);
|
|
614
|
-
if (!candidate)
|
|
615
|
-
continue;
|
|
616
|
-
if (tokens + candidate.tokens > maxTokens)
|
|
617
|
-
continue;
|
|
618
|
-
selected.push(candidate);
|
|
619
|
-
seen.add(id);
|
|
620
|
-
tokens += candidate.tokens;
|
|
621
|
-
if (tokens >= targetTokens) {
|
|
622
|
-
break;
|
|
623
|
-
}
|
|
624
|
-
}
|
|
625
|
-
return selected;
|
|
626
|
-
}
|
|
627
|
-
escapeXml(text) {
|
|
628
|
-
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
629
|
-
}
|
|
630
|
-
escapeXmlAttribute(text) {
|
|
631
|
-
return this.escapeXml(text).replace(/"/g, """).replace(/'/g, "'");
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
//# sourceMappingURL=session_maintenance_service.js.map
|