@oh-my-pi/pi-coding-agent 13.19.0 → 14.0.2
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 +266 -1
- package/package.json +86 -20
- package/scripts/format-prompts.ts +2 -2
- package/src/autoresearch/apply-contract-to-state.ts +24 -0
- package/src/autoresearch/contract.ts +0 -44
- package/src/autoresearch/dashboard.ts +1 -2
- package/src/autoresearch/git.ts +91 -0
- package/src/autoresearch/helpers.ts +49 -0
- package/src/autoresearch/index.ts +28 -187
- package/src/autoresearch/prompt.md +26 -9
- package/src/autoresearch/state.ts +0 -6
- package/src/autoresearch/tools/init-experiment.ts +202 -117
- package/src/autoresearch/tools/log-experiment.ts +83 -125
- package/src/autoresearch/tools/run-experiment.ts +48 -10
- package/src/autoresearch/types.ts +2 -2
- package/src/capability/index.ts +4 -2
- package/src/cli/file-processor.ts +3 -3
- package/src/cli/grep-cli.ts +8 -8
- package/src/cli/grievances-cli.ts +78 -0
- package/src/cli/read-cli.ts +67 -0
- package/src/cli/setup-cli.ts +4 -4
- package/src/cli/update-cli.ts +3 -3
- package/src/cli.ts +2 -0
- package/src/commands/grep.ts +6 -1
- package/src/commands/grievances.ts +20 -0
- package/src/commands/read.ts +33 -0
- package/src/commit/agentic/agent.ts +5 -5
- package/src/commit/agentic/index.ts +3 -4
- package/src/commit/agentic/tools/analyze-file.ts +3 -3
- package/src/commit/agentic/validation.ts +1 -1
- package/src/commit/analysis/conventional.ts +4 -4
- package/src/commit/analysis/summary.ts +3 -3
- package/src/commit/changelog/generate.ts +4 -4
- package/src/commit/map-reduce/map-phase.ts +4 -4
- package/src/commit/map-reduce/reduce-phase.ts +4 -4
- package/src/commit/pipeline.ts +3 -4
- package/src/config/prompt-templates.ts +44 -226
- package/src/config/resolve-config-value.ts +4 -2
- package/src/config/settings-schema.ts +54 -2
- package/src/config/settings.ts +25 -26
- package/src/dap/client.ts +674 -0
- package/src/dap/config.ts +150 -0
- package/src/dap/defaults.json +211 -0
- package/src/dap/index.ts +4 -0
- package/src/dap/session.ts +1255 -0
- package/src/dap/types.ts +600 -0
- package/src/debug/log-viewer.ts +3 -2
- package/src/discovery/builtin.ts +1 -2
- package/src/discovery/codex.ts +2 -2
- package/src/discovery/github.ts +2 -1
- package/src/discovery/helpers.ts +2 -2
- package/src/discovery/opencode.ts +2 -2
- package/src/edit/diff.ts +818 -0
- package/src/edit/index.ts +309 -0
- package/src/edit/line-hash.ts +67 -0
- package/src/edit/modes/chunk.ts +454 -0
- package/src/{patch → edit/modes}/hashline.ts +741 -361
- package/src/{patch/applicator.ts → edit/modes/patch.ts} +420 -117
- package/src/{patch/fuzzy.ts → edit/modes/replace.ts} +519 -197
- package/src/{patch → edit}/normalize.ts +97 -76
- package/src/{patch/shared.ts → edit/renderer.ts} +181 -108
- package/src/exec/bash-executor.ts +4 -2
- package/src/exec/idle-timeout-watchdog.ts +126 -0
- package/src/exec/non-interactive-env.ts +5 -0
- package/src/extensibility/custom-commands/bundled/ci-green/index.ts +2 -2
- package/src/extensibility/custom-commands/bundled/review/index.ts +2 -2
- package/src/extensibility/custom-commands/loader.ts +1 -2
- package/src/extensibility/custom-tools/loader.ts +34 -11
- package/src/extensibility/extensions/loader.ts +9 -4
- package/src/extensibility/extensions/runner.ts +24 -1
- package/src/extensibility/extensions/types.ts +1 -1
- package/src/extensibility/hooks/loader.ts +5 -6
- package/src/extensibility/hooks/types.ts +1 -1
- package/src/extensibility/plugins/doctor.ts +2 -1
- package/src/extensibility/slash-commands.ts +3 -7
- package/src/index.ts +2 -1
- package/src/internal-urls/docs-index.generated.ts +11 -11
- package/src/ipy/executor.ts +58 -17
- package/src/ipy/gateway-coordinator.ts +6 -4
- package/src/ipy/kernel.ts +45 -22
- package/src/ipy/runtime.ts +2 -2
- package/src/lsp/client.ts +7 -4
- package/src/lsp/clients/lsp-linter-client.ts +4 -4
- package/src/lsp/config.ts +2 -2
- package/src/lsp/defaults.json +688 -154
- package/src/lsp/index.ts +234 -45
- package/src/lsp/lspmux.ts +2 -2
- package/src/lsp/startup-events.ts +13 -0
- package/src/lsp/types.ts +12 -1
- package/src/lsp/utils.ts +8 -1
- package/src/main.ts +102 -46
- package/src/memories/index.ts +4 -5
- package/src/modes/acp/acp-agent.ts +563 -163
- package/src/modes/acp/acp-event-mapper.ts +9 -1
- package/src/modes/acp/acp-mode.ts +4 -2
- package/src/modes/components/agent-dashboard.ts +3 -4
- package/src/modes/components/diff.ts +6 -7
- package/src/modes/components/read-tool-group.ts +6 -12
- package/src/modes/components/settings-defs.ts +5 -0
- package/src/modes/components/tool-execution.ts +1 -1
- package/src/modes/components/welcome.ts +1 -1
- package/src/modes/controllers/btw-controller.ts +2 -2
- package/src/modes/controllers/command-controller.ts +3 -2
- package/src/modes/controllers/input-controller.ts +12 -8
- package/src/modes/index.ts +20 -2
- package/src/modes/interactive-mode.ts +94 -37
- package/src/modes/rpc/host-tools.ts +186 -0
- package/src/modes/rpc/rpc-client.ts +178 -13
- package/src/modes/rpc/rpc-mode.ts +73 -3
- package/src/modes/rpc/rpc-types.ts +53 -1
- package/src/modes/theme/theme.ts +80 -8
- package/src/modes/types.ts +2 -2
- package/src/prompts/system/system-prompt.md +2 -1
- package/src/prompts/tools/chunk-edit.md +219 -0
- package/src/prompts/tools/debug.md +43 -0
- package/src/prompts/tools/grep.md +3 -0
- package/src/prompts/tools/lsp.md +5 -5
- package/src/prompts/tools/read-chunk.md +17 -0
- package/src/prompts/tools/read.md +19 -5
- package/src/sdk.ts +190 -154
- package/src/secrets/obfuscator.ts +1 -1
- package/src/session/agent-session.ts +306 -256
- package/src/session/agent-storage.ts +12 -12
- package/src/session/compaction/branch-summarization.ts +3 -3
- package/src/session/compaction/compaction.ts +5 -6
- package/src/session/compaction/utils.ts +3 -3
- package/src/session/history-storage.ts +62 -19
- package/src/session/messages.ts +3 -3
- package/src/session/session-dump-format.ts +203 -0
- package/src/session/session-storage.ts +4 -2
- package/src/session/streaming-output.ts +1 -1
- package/src/session/tool-choice-queue.ts +213 -0
- package/src/slash-commands/builtin-registry.ts +56 -8
- package/src/ssh/connection-manager.ts +2 -2
- package/src/ssh/sshfs-mount.ts +5 -5
- package/src/stt/downloader.ts +4 -4
- package/src/stt/recorder.ts +4 -4
- package/src/stt/transcriber.ts +2 -2
- package/src/system-prompt.ts +21 -13
- package/src/task/agents.ts +5 -6
- package/src/task/commands.ts +2 -5
- package/src/task/executor.ts +4 -4
- package/src/task/index.ts +3 -4
- package/src/task/template.ts +2 -2
- package/src/task/worktree.ts +4 -4
- package/src/tools/ask.ts +2 -3
- package/src/tools/ast-edit.ts +7 -7
- package/src/tools/ast-grep.ts +7 -7
- package/src/tools/auto-generated-guard.ts +36 -41
- package/src/tools/await-tool.ts +2 -2
- package/src/tools/bash.ts +5 -23
- package/src/tools/browser.ts +4 -5
- package/src/tools/calculator.ts +2 -3
- package/src/tools/cancel-job.ts +2 -2
- package/src/tools/checkpoint.ts +3 -3
- package/src/tools/debug.ts +1007 -0
- package/src/tools/exit-plan-mode.ts +2 -3
- package/src/tools/fetch.ts +67 -3
- package/src/tools/find.ts +4 -5
- package/src/tools/fs-cache-invalidation.ts +5 -0
- package/src/tools/gemini-image.ts +13 -5
- package/src/tools/gh.ts +10 -11
- package/src/tools/grep.ts +57 -9
- package/src/tools/index.ts +44 -22
- package/src/tools/inspect-image.ts +4 -4
- package/src/tools/output-meta.ts +1 -1
- package/src/tools/python.ts +19 -6
- package/src/tools/read.ts +198 -67
- package/src/tools/render-mermaid.ts +2 -3
- package/src/tools/render-utils.ts +20 -6
- package/src/tools/renderers.ts +3 -1
- package/src/tools/report-tool-issue.ts +80 -0
- package/src/tools/resolve.ts +70 -39
- package/src/tools/search-tool-bm25.ts +2 -2
- package/src/tools/ssh.ts +2 -2
- package/src/tools/todo-write.ts +2 -2
- package/src/tools/tool-timeouts.ts +1 -0
- package/src/tools/write.ts +5 -6
- package/src/tui/tree-list.ts +3 -1
- package/src/utils/clipboard.ts +80 -0
- package/src/utils/commit-message-generator.ts +2 -3
- package/src/utils/edit-mode.ts +49 -0
- package/src/utils/file-display-mode.ts +6 -5
- package/src/utils/file-mentions.ts +8 -7
- package/src/utils/git.ts +4 -4
- package/src/utils/image-loading.ts +98 -0
- package/src/utils/title-generator.ts +2 -3
- package/src/utils/tools-manager.ts +6 -6
- package/src/web/scrapers/choosealicense.ts +1 -1
- package/src/web/search/index.ts +3 -3
- package/src/autoresearch/command-initialize.md +0 -34
- package/src/patch/diff.ts +0 -433
- package/src/patch/index.ts +0 -888
- package/src/patch/parser.ts +0 -532
- package/src/patch/types.ts +0 -292
- package/src/prompts/agents/oracle.md +0 -77
- package/src/tools/pending-action.ts +0 -49
- package/src/utils/child-process.ts +0 -88
- package/src/utils/frontmatter.ts +0 -117
- package/src/utils/image-input.ts +0 -274
- package/src/utils/mime.ts +0 -53
- package/src/utils/prompt-format.ts +0 -170
|
@@ -73,7 +73,7 @@ export class AgentStorage {
|
|
|
73
73
|
* AuthCredentialStore handles auth_credentials and cache tables.
|
|
74
74
|
*/
|
|
75
75
|
#initializeSchema(): void {
|
|
76
|
-
this.#db.
|
|
76
|
+
this.#db.run(`
|
|
77
77
|
PRAGMA journal_mode=WAL;
|
|
78
78
|
PRAGMA synchronous=NORMAL;
|
|
79
79
|
PRAGMA busy_timeout=5000;
|
|
@@ -92,7 +92,7 @@ CREATE TABLE IF NOT EXISTS schema_version (version INTEGER PRIMARY KEY);
|
|
|
92
92
|
const hasValue = settingsInfo.some(column => column.name === "value");
|
|
93
93
|
|
|
94
94
|
if (!hasSettingsTable) {
|
|
95
|
-
this.#db.
|
|
95
|
+
this.#db.run(`
|
|
96
96
|
CREATE TABLE settings (
|
|
97
97
|
key TEXT PRIMARY KEY,
|
|
98
98
|
value TEXT NOT NULL,
|
|
@@ -117,8 +117,8 @@ CREATE TABLE settings (
|
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
const migrate = this.#db.transaction((settings: Record<string, unknown> | null) => {
|
|
120
|
-
this.#db.
|
|
121
|
-
this.#db.
|
|
120
|
+
this.#db.run("DROP TABLE settings");
|
|
121
|
+
this.#db.run(`
|
|
122
122
|
CREATE TABLE settings (
|
|
123
123
|
key TEXT PRIMARY KEY,
|
|
124
124
|
value TEXT NOT NULL,
|
|
@@ -169,34 +169,34 @@ CREATE TABLE settings (
|
|
|
169
169
|
|
|
170
170
|
#migrateSchemaV4ToV5(): void {
|
|
171
171
|
const migrate = this.#db.transaction(() => {
|
|
172
|
-
this.#db.
|
|
173
|
-
this.#db.
|
|
172
|
+
this.#db.run("ALTER TABLE settings RENAME TO settings_legacy");
|
|
173
|
+
this.#db.run(`
|
|
174
174
|
CREATE TABLE settings (
|
|
175
175
|
key TEXT PRIMARY KEY,
|
|
176
176
|
value TEXT NOT NULL,
|
|
177
177
|
updated_at INTEGER NOT NULL DEFAULT (${SQLITE_NOW_EPOCH})
|
|
178
178
|
);
|
|
179
179
|
`);
|
|
180
|
-
this.#db.
|
|
180
|
+
this.#db.run(`
|
|
181
181
|
INSERT INTO settings (key, value, updated_at)
|
|
182
182
|
SELECT key, value, updated_at
|
|
183
183
|
FROM settings_legacy
|
|
184
184
|
`);
|
|
185
|
-
this.#db.
|
|
185
|
+
this.#db.run("DROP TABLE settings_legacy");
|
|
186
186
|
|
|
187
|
-
this.#db.
|
|
188
|
-
this.#db.
|
|
187
|
+
this.#db.run("ALTER TABLE model_usage RENAME TO model_usage_legacy");
|
|
188
|
+
this.#db.run(`
|
|
189
189
|
CREATE TABLE model_usage (
|
|
190
190
|
model_key TEXT PRIMARY KEY,
|
|
191
191
|
last_used_at INTEGER NOT NULL DEFAULT (${SQLITE_NOW_EPOCH})
|
|
192
192
|
);
|
|
193
193
|
`);
|
|
194
|
-
this.#db.
|
|
194
|
+
this.#db.run(`
|
|
195
195
|
INSERT INTO model_usage (model_key, last_used_at)
|
|
196
196
|
SELECT model_key, last_used_at
|
|
197
197
|
FROM model_usage_legacy
|
|
198
198
|
`);
|
|
199
|
-
this.#db.
|
|
199
|
+
this.#db.run("DROP TABLE model_usage_legacy");
|
|
200
200
|
});
|
|
201
201
|
migrate();
|
|
202
202
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
|
|
8
8
|
import type { Model } from "@oh-my-pi/pi-ai";
|
|
9
9
|
import { completeSimple } from "@oh-my-pi/pi-ai";
|
|
10
|
-
import {
|
|
10
|
+
import { prompt } from "@oh-my-pi/pi-utils";
|
|
11
11
|
import branchSummaryPrompt from "../../prompts/compaction/branch-summary.md" with { type: "text" };
|
|
12
12
|
import branchSummaryPreamble from "../../prompts/compaction/branch-summary-preamble.md" with { type: "text" };
|
|
13
13
|
import {
|
|
@@ -244,9 +244,9 @@ export function prepareBranchEntries(entries: SessionEntry[], tokenBudget: numbe
|
|
|
244
244
|
// Summary Generation
|
|
245
245
|
// ============================================================================
|
|
246
246
|
|
|
247
|
-
const BRANCH_SUMMARY_PREAMBLE =
|
|
247
|
+
const BRANCH_SUMMARY_PREAMBLE = prompt.render(branchSummaryPreamble);
|
|
248
248
|
|
|
249
|
-
const BRANCH_SUMMARY_PROMPT =
|
|
249
|
+
const BRANCH_SUMMARY_PROMPT = prompt.render(branchSummaryPrompt);
|
|
250
250
|
|
|
251
251
|
/**
|
|
252
252
|
* Generate a summary of abandoned branch entries.
|
|
@@ -26,8 +26,7 @@ import {
|
|
|
26
26
|
getOpenAIResponsesHistoryPayload,
|
|
27
27
|
normalizeResponsesToolCallId,
|
|
28
28
|
} from "@oh-my-pi/pi-ai/utils";
|
|
29
|
-
import { logger } from "@oh-my-pi/pi-utils";
|
|
30
|
-
import { renderPromptTemplate } from "../../config/prompt-templates";
|
|
29
|
+
import { logger, prompt } from "@oh-my-pi/pi-utils";
|
|
31
30
|
import compactionShortSummaryPrompt from "../../prompts/compaction/compaction-short-summary.md" with { type: "text" };
|
|
32
31
|
import compactionSummaryPrompt from "../../prompts/compaction/compaction-summary.md" with { type: "text" };
|
|
33
32
|
import compactionTurnPrefixPrompt from "../../prompts/compaction/compaction-turn-prefix.md" with { type: "text" };
|
|
@@ -474,11 +473,11 @@ export function findCutPoint(
|
|
|
474
473
|
// Summarization
|
|
475
474
|
// ============================================================================
|
|
476
475
|
|
|
477
|
-
const SUMMARIZATION_PROMPT =
|
|
476
|
+
const SUMMARIZATION_PROMPT = prompt.render(compactionSummaryPrompt);
|
|
478
477
|
|
|
479
|
-
const UPDATE_SUMMARIZATION_PROMPT =
|
|
478
|
+
const UPDATE_SUMMARIZATION_PROMPT = prompt.render(compactionUpdateSummaryPrompt);
|
|
480
479
|
|
|
481
|
-
const SHORT_SUMMARY_PROMPT =
|
|
480
|
+
const SHORT_SUMMARY_PROMPT = prompt.render(compactionShortSummaryPrompt);
|
|
482
481
|
|
|
483
482
|
function formatAdditionalContext(context: string[] | undefined): string {
|
|
484
483
|
if (!context || context.length === 0) return "";
|
|
@@ -1201,7 +1200,7 @@ export function prepareCompaction(
|
|
|
1201
1200
|
// Main compaction function
|
|
1202
1201
|
// ============================================================================
|
|
1203
1202
|
|
|
1204
|
-
const TURN_PREFIX_SUMMARIZATION_PROMPT =
|
|
1203
|
+
const TURN_PREFIX_SUMMARIZATION_PROMPT = prompt.render(compactionTurnPrefixPrompt);
|
|
1205
1204
|
|
|
1206
1205
|
/**
|
|
1207
1206
|
* Generate summaries for compaction using prepared data.
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
|
|
5
5
|
import type { Message } from "@oh-my-pi/pi-ai";
|
|
6
|
-
import {
|
|
6
|
+
import { prompt } from "@oh-my-pi/pi-utils";
|
|
7
7
|
import fileOperationsTemplate from "../../prompts/system/file-operations.md" with { type: "text" };
|
|
8
8
|
import summarizationSystemPrompt from "../../prompts/system/summarization-system.md" with { type: "text" };
|
|
9
9
|
|
|
@@ -86,7 +86,7 @@ function stripFileOperationTags(summary: string): string {
|
|
|
86
86
|
}
|
|
87
87
|
export function formatFileOperations(readFiles: string[], modifiedFiles: string[]): string {
|
|
88
88
|
if (readFiles.length === 0 && modifiedFiles.length === 0) return "";
|
|
89
|
-
return
|
|
89
|
+
return prompt.render(fileOperationsTemplate, {
|
|
90
90
|
readFiles: truncateFileList(readFiles),
|
|
91
91
|
modifiedFiles: truncateFileList(modifiedFiles),
|
|
92
92
|
});
|
|
@@ -181,4 +181,4 @@ export function serializeConversation(messages: Message[]): string {
|
|
|
181
181
|
// Summarization System Prompt
|
|
182
182
|
// ============================================================================
|
|
183
183
|
|
|
184
|
-
export const SUMMARIZATION_SYSTEM_PROMPT =
|
|
184
|
+
export const SUMMARIZATION_SYSTEM_PROMPT = prompt.render(summarizationSystemPrompt);
|
|
@@ -19,12 +19,47 @@ type HistoryRow = {
|
|
|
19
19
|
|
|
20
20
|
const SQLITE_NOW_EPOCH = "CAST(strftime('%s','now') AS INTEGER)";
|
|
21
21
|
|
|
22
|
+
class AsyncDrain<T> {
|
|
23
|
+
#queue?: T[];
|
|
24
|
+
#promise = Promise.resolve();
|
|
25
|
+
|
|
26
|
+
constructor(readonly delayMs: number = 0) {}
|
|
27
|
+
|
|
28
|
+
push(value: T, hnd: (values: T[]) => Promise<void> | void): Promise<void> {
|
|
29
|
+
let queue = this.#queue;
|
|
30
|
+
if (!queue) {
|
|
31
|
+
this.#queue = queue = [];
|
|
32
|
+
this.#promise = new Promise((resolve, reject) => {
|
|
33
|
+
const exec = () => {
|
|
34
|
+
try {
|
|
35
|
+
if (this.#queue === queue) {
|
|
36
|
+
this.#queue = undefined;
|
|
37
|
+
}
|
|
38
|
+
resolve(hnd(queue!));
|
|
39
|
+
} catch (error) {
|
|
40
|
+
reject(error);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
if (this.delayMs > 0) {
|
|
45
|
+
setTimeout(exec, this.delayMs);
|
|
46
|
+
} else {
|
|
47
|
+
queueMicrotask(exec);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
queue.push(value);
|
|
52
|
+
return this.#promise;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
22
56
|
export class HistoryStorage {
|
|
23
57
|
#db: Database;
|
|
24
58
|
static #instance?: HistoryStorage;
|
|
59
|
+
#drain = new AsyncDrain<Pick<HistoryEntry, "prompt" | "cwd">>(100);
|
|
25
60
|
|
|
26
61
|
// Prepared statements
|
|
27
|
-
#
|
|
62
|
+
#insertRowStmt: Statement;
|
|
28
63
|
#recentStmt: Statement;
|
|
29
64
|
#searchStmt: Statement;
|
|
30
65
|
#lastPromptStmt: Statement;
|
|
@@ -39,7 +74,7 @@ export class HistoryStorage {
|
|
|
39
74
|
|
|
40
75
|
const hasFts = this.#db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='history_fts'").get();
|
|
41
76
|
|
|
42
|
-
this.#db.
|
|
77
|
+
this.#db.run(`
|
|
43
78
|
PRAGMA journal_mode=WAL;
|
|
44
79
|
PRAGMA synchronous=NORMAL;
|
|
45
80
|
PRAGMA busy_timeout=5000;
|
|
@@ -71,7 +106,6 @@ CREATE TRIGGER IF NOT EXISTS history_ai AFTER INSERT ON history BEGIN
|
|
|
71
106
|
}
|
|
72
107
|
}
|
|
73
108
|
|
|
74
|
-
this.#insertStmt = this.#db.prepare("INSERT INTO history (prompt, cwd) VALUES (?, ?)");
|
|
75
109
|
this.#recentStmt = this.#db.prepare(
|
|
76
110
|
"SELECT id, prompt, created_at, cwd FROM history ORDER BY created_at DESC, id DESC LIMIT ?",
|
|
77
111
|
);
|
|
@@ -80,6 +114,8 @@ CREATE TRIGGER IF NOT EXISTS history_ai AFTER INSERT ON history BEGIN
|
|
|
80
114
|
);
|
|
81
115
|
this.#lastPromptStmt = this.#db.prepare("SELECT prompt FROM history ORDER BY id DESC LIMIT 1");
|
|
82
116
|
|
|
117
|
+
this.#insertRowStmt = this.#db.prepare("INSERT INTO history (prompt, cwd) VALUES (?, ?)");
|
|
118
|
+
|
|
83
119
|
const last = this.#lastPromptStmt.get() as { prompt?: string } | undefined;
|
|
84
120
|
this.#lastPromptCache = last?.prompt ?? null;
|
|
85
121
|
}
|
|
@@ -91,19 +127,26 @@ CREATE TRIGGER IF NOT EXISTS history_ai AFTER INSERT ON history BEGIN
|
|
|
91
127
|
return HistoryStorage.#instance;
|
|
92
128
|
}
|
|
93
129
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
this.#lastPromptCache = trimmed;
|
|
130
|
+
/** @internal Reset the singleton — test-only. */
|
|
131
|
+
static resetInstance(): void {
|
|
132
|
+
HistoryStorage.#instance = undefined;
|
|
133
|
+
}
|
|
100
134
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
logger.error("HistoryStorage add failed", { error: String(error) });
|
|
135
|
+
#insertBatch(rows: Array<Pick<HistoryEntry, "prompt" | "cwd">>): void {
|
|
136
|
+
this.#db.transaction((rows: Array<Pick<HistoryEntry, "prompt" | "cwd">>) => {
|
|
137
|
+
for (const row of rows) {
|
|
138
|
+
this.#insertRowStmt.run(row.prompt, row.cwd ?? null);
|
|
106
139
|
}
|
|
140
|
+
})(rows);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
add(prompt: string, cwd?: string): Promise<void> {
|
|
144
|
+
const trimmed = prompt.trim();
|
|
145
|
+
if (!trimmed) return Promise.resolve();
|
|
146
|
+
if (this.#lastPromptCache === trimmed) return Promise.resolve();
|
|
147
|
+
this.#lastPromptCache = trimmed;
|
|
148
|
+
return this.#drain.push({ prompt: trimmed, cwd: cwd ?? undefined }, rows => {
|
|
149
|
+
this.#insertBatch(rows);
|
|
107
150
|
});
|
|
108
151
|
}
|
|
109
152
|
|
|
@@ -150,11 +193,11 @@ CREATE TRIGGER IF NOT EXISTS history_ai AFTER INSERT ON history BEGIN
|
|
|
150
193
|
|
|
151
194
|
#migrateHistorySchema(): void {
|
|
152
195
|
const migrate = this.#db.transaction(() => {
|
|
153
|
-
this.#db.
|
|
154
|
-
this.#db.
|
|
155
|
-
this.#db.
|
|
156
|
-
this.#db.
|
|
157
|
-
this.#db.
|
|
196
|
+
this.#db.run("ALTER TABLE history RENAME TO history_legacy");
|
|
197
|
+
this.#db.run("DROP INDEX IF EXISTS idx_history_created_at");
|
|
198
|
+
this.#db.run("DROP TRIGGER IF EXISTS history_ai");
|
|
199
|
+
this.#db.run("DROP TABLE IF EXISTS history_fts");
|
|
200
|
+
this.#db.run(`
|
|
158
201
|
CREATE TABLE history (
|
|
159
202
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
160
203
|
prompt TEXT NOT NULL,
|
package/src/session/messages.ts
CHANGED
|
@@ -14,7 +14,7 @@ import type {
|
|
|
14
14
|
TextContent,
|
|
15
15
|
ToolResultMessage,
|
|
16
16
|
} from "@oh-my-pi/pi-ai";
|
|
17
|
-
import {
|
|
17
|
+
import { prompt } from "@oh-my-pi/pi-utils";
|
|
18
18
|
import branchSummaryContextPrompt from "../prompts/compaction/branch-summary-context.md" with { type: "text" };
|
|
19
19
|
import compactionSummaryContextPrompt from "../prompts/compaction/compaction-summary-context.md" with { type: "text" };
|
|
20
20
|
import type { OutputMeta } from "../tools/output-meta";
|
|
@@ -310,7 +310,7 @@ export function convertToLlm(messages: AgentMessage[]): Message[] {
|
|
|
310
310
|
content: [
|
|
311
311
|
{
|
|
312
312
|
type: "text" as const,
|
|
313
|
-
text:
|
|
313
|
+
text: prompt.render(BRANCH_SUMMARY_TEMPLATE, { summary: m.summary }),
|
|
314
314
|
},
|
|
315
315
|
],
|
|
316
316
|
attribution: "agent",
|
|
@@ -322,7 +322,7 @@ export function convertToLlm(messages: AgentMessage[]): Message[] {
|
|
|
322
322
|
content: [
|
|
323
323
|
{
|
|
324
324
|
type: "text" as const,
|
|
325
|
-
text:
|
|
325
|
+
text: prompt.render(COMPACTION_SUMMARY_TEMPLATE, { summary: m.summary }),
|
|
326
326
|
},
|
|
327
327
|
],
|
|
328
328
|
attribution: "agent",
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plain-text / markdown session formatting (same shape as /dump clipboard export).
|
|
3
|
+
*/
|
|
4
|
+
import type { AgentMessage, ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
5
|
+
import { INTENT_FIELD } from "@oh-my-pi/pi-agent-core";
|
|
6
|
+
import type { AssistantMessage, Model } from "@oh-my-pi/pi-ai";
|
|
7
|
+
import {
|
|
8
|
+
type BashExecutionMessage,
|
|
9
|
+
type BranchSummaryMessage,
|
|
10
|
+
bashExecutionToText,
|
|
11
|
+
type CompactionSummaryMessage,
|
|
12
|
+
type CustomMessage,
|
|
13
|
+
type FileMentionMessage,
|
|
14
|
+
type HookMessage,
|
|
15
|
+
type PythonExecutionMessage,
|
|
16
|
+
pythonExecutionToText,
|
|
17
|
+
} from "./messages";
|
|
18
|
+
|
|
19
|
+
/** Minimal tool shape for dump output (matches AgentTool fields used by formatSessionDumpText). */
|
|
20
|
+
export interface SessionDumpToolInfo {
|
|
21
|
+
name: string;
|
|
22
|
+
description: string;
|
|
23
|
+
parameters: unknown;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface FormatSessionDumpTextOptions {
|
|
27
|
+
messages: readonly AgentMessage[];
|
|
28
|
+
systemPrompt?: string | null;
|
|
29
|
+
model?: Model | null;
|
|
30
|
+
thinkingLevel?: ThinkingLevel | string | null;
|
|
31
|
+
tools?: readonly SessionDumpToolInfo[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function stripTypeBoxFields(obj: unknown): unknown {
|
|
35
|
+
if (Array.isArray(obj)) {
|
|
36
|
+
return obj.map(stripTypeBoxFields);
|
|
37
|
+
}
|
|
38
|
+
if (obj && typeof obj === "object") {
|
|
39
|
+
const result: Record<string, unknown> = {};
|
|
40
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
41
|
+
if (!k.startsWith("TypeBox.")) {
|
|
42
|
+
result[k] = stripTypeBoxFields(v);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
return obj;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Serialize an object as XML parameter elements, one per key. */
|
|
51
|
+
function formatArgsAsXml(args: Record<string, unknown>, indent = "\t"): string {
|
|
52
|
+
const parts: string[] = [];
|
|
53
|
+
for (const [key, value] of Object.entries(args)) {
|
|
54
|
+
if (key === INTENT_FIELD) continue;
|
|
55
|
+
const text = typeof value === "string" ? value : JSON.stringify(value);
|
|
56
|
+
parts.push(`${indent}<parameter name="${key}">${text}</parameter>`);
|
|
57
|
+
}
|
|
58
|
+
return parts.join("\n");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Format messages and session metadata as markdown/plain text (same as AgentSession.formatSessionAsText / /dump).
|
|
63
|
+
*/
|
|
64
|
+
export function formatSessionDumpText(options: FormatSessionDumpTextOptions): string {
|
|
65
|
+
const lines: string[] = [];
|
|
66
|
+
|
|
67
|
+
const systemPrompt = options.systemPrompt;
|
|
68
|
+
if (systemPrompt) {
|
|
69
|
+
lines.push("## System Prompt\n");
|
|
70
|
+
lines.push(systemPrompt);
|
|
71
|
+
lines.push("\n");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const model = options.model;
|
|
75
|
+
const thinkingLevel = options.thinkingLevel;
|
|
76
|
+
lines.push("## Configuration\n");
|
|
77
|
+
lines.push(`Model: ${model ? `${model.provider}/${model.id}` : "(not selected)"}`);
|
|
78
|
+
lines.push(`Thinking Level: ${thinkingLevel ?? ""}`);
|
|
79
|
+
lines.push("\n");
|
|
80
|
+
|
|
81
|
+
const tools = options.tools ?? [];
|
|
82
|
+
if (tools.length > 0) {
|
|
83
|
+
lines.push("## Available Tools\n");
|
|
84
|
+
for (const tool of tools) {
|
|
85
|
+
lines.push(`<tool name="${tool.name}">`);
|
|
86
|
+
lines.push(tool.description);
|
|
87
|
+
const parametersClean = stripTypeBoxFields(tool.parameters);
|
|
88
|
+
lines.push(`\nParameters:\n${formatArgsAsXml(parametersClean as Record<string, unknown>)}`);
|
|
89
|
+
lines.push("<" + "/tool>\n");
|
|
90
|
+
}
|
|
91
|
+
lines.push("\n");
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
for (const msg of options.messages) {
|
|
95
|
+
if (msg.role === "user" || msg.role === "developer") {
|
|
96
|
+
lines.push(msg.role === "developer" ? "## Developer\n" : "## User\n");
|
|
97
|
+
if (typeof msg.content === "string") {
|
|
98
|
+
lines.push(msg.content);
|
|
99
|
+
} else {
|
|
100
|
+
for (const c of msg.content) {
|
|
101
|
+
if (c.type === "text") {
|
|
102
|
+
lines.push(c.text);
|
|
103
|
+
} else if (c.type === "image") {
|
|
104
|
+
lines.push("[Image]");
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
lines.push("\n");
|
|
109
|
+
} else if (msg.role === "assistant") {
|
|
110
|
+
const assistantMsg = msg as AssistantMessage;
|
|
111
|
+
lines.push("## Assistant\n");
|
|
112
|
+
|
|
113
|
+
for (const c of assistantMsg.content) {
|
|
114
|
+
if (c.type === "text") {
|
|
115
|
+
lines.push(c.text);
|
|
116
|
+
} else if (c.type === "thinking") {
|
|
117
|
+
lines.push("<thinking>");
|
|
118
|
+
lines.push(c.thinking);
|
|
119
|
+
lines.push("</thinking>\n");
|
|
120
|
+
} else if (c.type === "toolCall") {
|
|
121
|
+
lines.push(`<invoke name="${c.name}">`);
|
|
122
|
+
if (c.arguments && typeof c.arguments === "object") {
|
|
123
|
+
lines.push(formatArgsAsXml(c.arguments as Record<string, unknown>));
|
|
124
|
+
}
|
|
125
|
+
lines.push("<" + "/invoke>\n");
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
lines.push("");
|
|
129
|
+
} else if (msg.role === "toolResult") {
|
|
130
|
+
lines.push(`### Tool Result: ${msg.toolName}`);
|
|
131
|
+
if (msg.isError) {
|
|
132
|
+
lines.push("(error)");
|
|
133
|
+
}
|
|
134
|
+
for (const c of msg.content) {
|
|
135
|
+
if (c.type === "text") {
|
|
136
|
+
lines.push("```");
|
|
137
|
+
lines.push(c.text);
|
|
138
|
+
lines.push("```");
|
|
139
|
+
} else if (c.type === "image") {
|
|
140
|
+
lines.push("[Image output]");
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
lines.push("");
|
|
144
|
+
} else if (msg.role === "bashExecution") {
|
|
145
|
+
const bashMsg = msg as BashExecutionMessage;
|
|
146
|
+
if (!bashMsg.excludeFromContext) {
|
|
147
|
+
lines.push("## Bash Execution\n");
|
|
148
|
+
lines.push(bashExecutionToText(bashMsg));
|
|
149
|
+
lines.push("\n");
|
|
150
|
+
}
|
|
151
|
+
} else if (msg.role === "pythonExecution") {
|
|
152
|
+
const pythonMsg = msg as PythonExecutionMessage;
|
|
153
|
+
if (!pythonMsg.excludeFromContext) {
|
|
154
|
+
lines.push("## Python Execution\n");
|
|
155
|
+
lines.push(pythonExecutionToText(pythonMsg));
|
|
156
|
+
lines.push("\n");
|
|
157
|
+
}
|
|
158
|
+
} else if (msg.role === "custom" || msg.role === "hookMessage") {
|
|
159
|
+
const customMsg = msg as CustomMessage | HookMessage;
|
|
160
|
+
lines.push(`## ${customMsg.customType}\n`);
|
|
161
|
+
if (typeof customMsg.content === "string") {
|
|
162
|
+
lines.push(customMsg.content);
|
|
163
|
+
} else {
|
|
164
|
+
for (const c of customMsg.content) {
|
|
165
|
+
if (c.type === "text") {
|
|
166
|
+
lines.push(c.text);
|
|
167
|
+
} else if (c.type === "image") {
|
|
168
|
+
lines.push("[Image]");
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
lines.push("\n");
|
|
173
|
+
} else if (msg.role === "branchSummary") {
|
|
174
|
+
const branchMsg = msg as BranchSummaryMessage;
|
|
175
|
+
lines.push("## Branch Summary\n");
|
|
176
|
+
lines.push(`(from branch: ${branchMsg.fromId})\n`);
|
|
177
|
+
lines.push(branchMsg.summary);
|
|
178
|
+
lines.push("\n");
|
|
179
|
+
} else if (msg.role === "compactionSummary") {
|
|
180
|
+
const compactMsg = msg as CompactionSummaryMessage;
|
|
181
|
+
lines.push("## Compaction Summary\n");
|
|
182
|
+
lines.push(`(${compactMsg.tokensBefore} tokens before compaction)\n`);
|
|
183
|
+
lines.push(compactMsg.summary);
|
|
184
|
+
lines.push("\n");
|
|
185
|
+
} else if (msg.role === "fileMention") {
|
|
186
|
+
const fileMsg = msg as FileMentionMessage;
|
|
187
|
+
lines.push("## File Mention\n");
|
|
188
|
+
for (const file of fileMsg.files) {
|
|
189
|
+
lines.push(`<file path="${file.path}">`);
|
|
190
|
+
if (file.content) {
|
|
191
|
+
lines.push(file.content);
|
|
192
|
+
}
|
|
193
|
+
if (file.image) {
|
|
194
|
+
lines.push("[Image attached]");
|
|
195
|
+
}
|
|
196
|
+
lines.push("</file>\n");
|
|
197
|
+
}
|
|
198
|
+
lines.push("\n");
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return lines.join("\n").trim();
|
|
203
|
+
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as fsp from "node:fs/promises";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
-
import { isEnoent, toError } from "@oh-my-pi/pi-utils";
|
|
4
|
+
import { isEnoent, peekFile, toError } from "@oh-my-pi/pi-utils";
|
|
5
|
+
|
|
6
|
+
const utf8Decoder = new TextDecoder("utf-8");
|
|
5
7
|
|
|
6
8
|
export interface SessionStorageStat {
|
|
7
9
|
size: number;
|
|
@@ -163,7 +165,7 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
163
165
|
}
|
|
164
166
|
|
|
165
167
|
async readTextPrefix(path: string, maxBytes: number): Promise<string> {
|
|
166
|
-
return
|
|
168
|
+
return peekFile(path, maxBytes, header => utf8Decoder.decode(header));
|
|
167
169
|
}
|
|
168
170
|
|
|
169
171
|
async writeText(path: string, content: string): Promise<void> {
|
|
@@ -747,6 +747,6 @@ export function formatHeadTruncationNotice(
|
|
|
747
747
|
const totalFileLines = options.totalFileLines ?? truncation.totalLines;
|
|
748
748
|
const endLineDisplay = startLineDisplay + (truncation.outputLines ?? truncation.totalLines) - 1;
|
|
749
749
|
const nextOffset = endLineDisplay + 1;
|
|
750
|
-
const notice = `[Showing lines ${startLineDisplay}-${endLineDisplay} of ${totalFileLines}. Use
|
|
750
|
+
const notice = `[Showing lines ${startLineDisplay}-${endLineDisplay} of ${totalFileLines}. Use sel=L${nextOffset} to continue]`;
|
|
751
751
|
return `\n\n${notice}`;
|
|
752
752
|
}
|