@codex-infinity/pi-infinity 0.64.1 → 0.64.3
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 +76 -0
- package/README.md +10 -4
- package/dist/core/agent-session-runtime.d.ts +136 -0
- package/dist/core/agent-session-runtime.d.ts.map +1 -0
- package/dist/core/agent-session-runtime.js +265 -0
- package/dist/core/agent-session-runtime.js.map +1 -0
- package/dist/core/agent-session.d.ts +5 -42
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +13 -207
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/export-html/tool-renderer.d.ts +2 -0
- package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
- package/dist/core/export-html/tool-renderer.js +2 -2
- package/dist/core/export-html/tool-renderer.js.map +1 -1
- package/dist/core/extensions/index.d.ts +2 -2
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/types.d.ts +16 -16
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js +10 -0
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts +5 -1
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +69 -8
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/index.d.ts +2 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +2 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/sdk.d.ts +4 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +3 -0
- package/dist/core/sdk.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +42 -9
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts +1 -0
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +4 -1
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +8 -4
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +89 -86
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +6 -11
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/print-mode.d.ts +2 -2
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +37 -36
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts +2 -2
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +69 -49
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/docs/extensions.md +75 -19
- package/docs/sdk.md +160 -72
- package/docs/tree.md +1 -1
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/custom-provider-qwen-cli/package.json +1 -1
- package/examples/extensions/hello.ts +18 -17
- package/examples/extensions/hidden-thinking-label.ts +0 -4
- package/examples/extensions/rpc-demo.ts +3 -9
- package/examples/extensions/status-line.ts +0 -8
- package/examples/extensions/todo.ts +0 -2
- package/examples/extensions/tools.ts +0 -5
- package/examples/extensions/widget-placement.ts +4 -12
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/sdk/13-session-runtime.ts +49 -0
- package/examples/sdk/README.md +2 -1
- package/package.json +4 -4
|
@@ -12,14 +12,14 @@
|
|
|
12
12
|
*
|
|
13
13
|
* Modes use this class and add their own I/O layer on top.
|
|
14
14
|
*/
|
|
15
|
-
import {
|
|
15
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
16
16
|
import { basename, dirname, join, resolve } from "node:path";
|
|
17
17
|
import { isContextOverflow, modelsAreEqual, resetApiProviders, supportsXhigh } from "@mariozechner/pi-ai";
|
|
18
18
|
import { getDocsPath } from "../config.js";
|
|
19
19
|
import { theme } from "../modes/interactive/theme/theme.js";
|
|
20
20
|
import { stripFrontmatter } from "../utils/frontmatter.js";
|
|
21
21
|
import { sleep } from "../utils/sleep.js";
|
|
22
|
-
import {
|
|
22
|
+
import { executeBashWithOperations } from "./bash-executor.js";
|
|
23
23
|
import { calculateContextTokens, collectEntriesForBranchSummary, compact, estimateContextTokens, generateBranchSummary, prepareCompaction, shouldCompact, } from "./compaction/index.js";
|
|
24
24
|
import { DEFAULT_THINKING_LEVEL } from "./defaults.js";
|
|
25
25
|
import { exportSessionToHtml } from "./export-html/index.js";
|
|
@@ -29,6 +29,7 @@ import { expandPromptTemplate } from "./prompt-templates.js";
|
|
|
29
29
|
import { CURRENT_SESSION_VERSION, getLatestCompactionEntry } from "./session-manager.js";
|
|
30
30
|
import { createSyntheticSourceInfo } from "./source-info.js";
|
|
31
31
|
import { buildSystemPrompt } from "./system-prompt.js";
|
|
32
|
+
import { createLocalBashOperations } from "./tools/bash.js";
|
|
32
33
|
import { createAllToolDefinitions } from "./tools/index.js";
|
|
33
34
|
import { createToolDefinitionFromAgentTool, wrapToolDefinition } from "./tools/tool-definition-wrapper.js";
|
|
34
35
|
/**
|
|
@@ -121,6 +122,7 @@ export class AgentSession {
|
|
|
121
122
|
this._baseToolsOverride = config.baseToolsOverride;
|
|
122
123
|
this._autoNextSteps = config.autoNextSteps ?? false;
|
|
123
124
|
this._autoNextIdea = config.autoNextIdea ?? false;
|
|
125
|
+
this._sessionStartEvent = config.sessionStartEvent ?? { type: "session_start", reason: "startup" };
|
|
124
126
|
// Always subscribe to agent events for internal handling
|
|
125
127
|
// (session persistence, extensions, auto-compaction, retry logic)
|
|
126
128
|
this._unsubscribeAgent = this.agent.subscribe(this._handleAgentEvent);
|
|
@@ -1015,54 +1017,6 @@ export class AgentSession {
|
|
|
1015
1017
|
this.agent.abort();
|
|
1016
1018
|
await this.agent.waitForIdle();
|
|
1017
1019
|
}
|
|
1018
|
-
/**
|
|
1019
|
-
* Start a new session, optionally with initial messages and parent tracking.
|
|
1020
|
-
* Clears all messages and starts a new session.
|
|
1021
|
-
* Listeners are preserved and will continue receiving events.
|
|
1022
|
-
* @param options.parentSession - Optional parent session path for tracking
|
|
1023
|
-
* @param options.setup - Optional callback to initialize session (e.g., append messages)
|
|
1024
|
-
* @returns true if completed, false if cancelled by extension
|
|
1025
|
-
*/
|
|
1026
|
-
async newSession(options) {
|
|
1027
|
-
const previousSessionFile = this.sessionFile;
|
|
1028
|
-
// Emit session_before_switch event with reason "new" (can be cancelled)
|
|
1029
|
-
if (this._extensionRunner?.hasHandlers("session_before_switch")) {
|
|
1030
|
-
const result = (await this._extensionRunner.emit({
|
|
1031
|
-
type: "session_before_switch",
|
|
1032
|
-
reason: "new",
|
|
1033
|
-
}));
|
|
1034
|
-
if (result?.cancel) {
|
|
1035
|
-
return false;
|
|
1036
|
-
}
|
|
1037
|
-
}
|
|
1038
|
-
this._disconnectFromAgent();
|
|
1039
|
-
await this.abort();
|
|
1040
|
-
this.agent.reset();
|
|
1041
|
-
this.sessionManager.newSession({ parentSession: options?.parentSession });
|
|
1042
|
-
this.agent.sessionId = this.sessionManager.getSessionId();
|
|
1043
|
-
this._steeringMessages = [];
|
|
1044
|
-
this._followUpMessages = [];
|
|
1045
|
-
this._pendingNextTurnMessages = [];
|
|
1046
|
-
this.sessionManager.appendThinkingLevelChange(this.thinkingLevel);
|
|
1047
|
-
// Run setup callback if provided (e.g., to append initial messages)
|
|
1048
|
-
if (options?.setup) {
|
|
1049
|
-
await options.setup(this.sessionManager);
|
|
1050
|
-
// Sync agent state with session manager after setup
|
|
1051
|
-
const sessionContext = this.sessionManager.buildSessionContext();
|
|
1052
|
-
this.agent.state.messages = sessionContext.messages;
|
|
1053
|
-
}
|
|
1054
|
-
this._reconnectToAgent();
|
|
1055
|
-
// Emit session_switch event with reason "new" to extensions
|
|
1056
|
-
if (this._extensionRunner) {
|
|
1057
|
-
await this._extensionRunner.emit({
|
|
1058
|
-
type: "session_switch",
|
|
1059
|
-
reason: "new",
|
|
1060
|
-
previousSessionFile,
|
|
1061
|
-
});
|
|
1062
|
-
}
|
|
1063
|
-
// Emit session event to custom tools
|
|
1064
|
-
return true;
|
|
1065
|
-
}
|
|
1066
1020
|
// =========================================================================
|
|
1067
1021
|
// Model Management
|
|
1068
1022
|
// =========================================================================
|
|
@@ -1108,11 +1062,8 @@ export class AgentSession {
|
|
|
1108
1062
|
}
|
|
1109
1063
|
return this._cycleAvailableModel(direction);
|
|
1110
1064
|
}
|
|
1111
|
-
_getScopedModelsWithAuth() {
|
|
1112
|
-
return this._scopedModels.filter((scoped) => this._modelRegistry.hasConfiguredAuth(scoped.model));
|
|
1113
|
-
}
|
|
1114
1065
|
async _cycleScopedModel(direction) {
|
|
1115
|
-
const scopedModels = this.
|
|
1066
|
+
const scopedModels = this._scopedModels.filter((scoped) => this._modelRegistry.hasConfiguredAuth(scoped.model));
|
|
1116
1067
|
if (scopedModels.length <= 1)
|
|
1117
1068
|
return undefined;
|
|
1118
1069
|
const currentModel = this.model;
|
|
@@ -1644,8 +1595,8 @@ export class AgentSession {
|
|
|
1644
1595
|
}
|
|
1645
1596
|
if (this._extensionRunner) {
|
|
1646
1597
|
this._applyExtensionBindings(this._extensionRunner);
|
|
1647
|
-
await this._extensionRunner.emit(
|
|
1648
|
-
await this.extendResourcesFromExtensions("startup");
|
|
1598
|
+
await this._extensionRunner.emit(this._sessionStartEvent);
|
|
1599
|
+
await this.extendResourcesFromExtensions(this._sessionStartEvent.reason === "reload" ? "reload" : "startup");
|
|
1649
1600
|
}
|
|
1650
1601
|
}
|
|
1651
1602
|
async extendResourcesFromExtensions(reason) {
|
|
@@ -1929,7 +1880,7 @@ export class AgentSession {
|
|
|
1929
1880
|
this._extensionShutdownHandler ||
|
|
1930
1881
|
this._extensionErrorListener;
|
|
1931
1882
|
if (this._extensionRunner && hasBindings) {
|
|
1932
|
-
await this._extensionRunner.emit({ type: "session_start" });
|
|
1883
|
+
await this._extensionRunner.emit({ type: "session_start", reason: "reload" });
|
|
1933
1884
|
await this.extendResourcesFromExtensions("reload");
|
|
1934
1885
|
}
|
|
1935
1886
|
}
|
|
@@ -2110,15 +2061,10 @@ export class AgentSession {
|
|
|
2110
2061
|
const prefix = this.settingsManager.getShellCommandPrefix();
|
|
2111
2062
|
const resolvedCommand = prefix ? `${prefix}\n${command}` : command;
|
|
2112
2063
|
try {
|
|
2113
|
-
const result = options?.operations
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
})
|
|
2118
|
-
: await executeBashCommand(resolvedCommand, {
|
|
2119
|
-
onChunk,
|
|
2120
|
-
signal: this._bashAbortController.signal,
|
|
2121
|
-
});
|
|
2064
|
+
const result = await executeBashWithOperations(resolvedCommand, this.sessionManager.getCwd(), options?.operations ?? createLocalBashOperations(), {
|
|
2065
|
+
onChunk,
|
|
2066
|
+
signal: this._bashAbortController.signal,
|
|
2067
|
+
});
|
|
2122
2068
|
this.recordBashResult(command, result, options);
|
|
2123
2069
|
return result;
|
|
2124
2070
|
}
|
|
@@ -2186,130 +2132,12 @@ export class AgentSession {
|
|
|
2186
2132
|
// =========================================================================
|
|
2187
2133
|
// Session Management
|
|
2188
2134
|
// =========================================================================
|
|
2189
|
-
/**
|
|
2190
|
-
* Switch to a different session file.
|
|
2191
|
-
* Aborts current operation, loads messages, restores model/thinking.
|
|
2192
|
-
* Listeners are preserved and will continue receiving events.
|
|
2193
|
-
* @returns true if switch completed, false if cancelled by extension
|
|
2194
|
-
*/
|
|
2195
|
-
async switchSession(sessionPath) {
|
|
2196
|
-
const previousSessionFile = this.sessionManager.getSessionFile();
|
|
2197
|
-
// Emit session_before_switch event (can be cancelled)
|
|
2198
|
-
if (this._extensionRunner?.hasHandlers("session_before_switch")) {
|
|
2199
|
-
const result = (await this._extensionRunner.emit({
|
|
2200
|
-
type: "session_before_switch",
|
|
2201
|
-
reason: "resume",
|
|
2202
|
-
targetSessionFile: sessionPath,
|
|
2203
|
-
}));
|
|
2204
|
-
if (result?.cancel) {
|
|
2205
|
-
return false;
|
|
2206
|
-
}
|
|
2207
|
-
}
|
|
2208
|
-
this._disconnectFromAgent();
|
|
2209
|
-
await this.abort();
|
|
2210
|
-
this._steeringMessages = [];
|
|
2211
|
-
this._followUpMessages = [];
|
|
2212
|
-
this._pendingNextTurnMessages = [];
|
|
2213
|
-
// Set new session
|
|
2214
|
-
this.sessionManager.setSessionFile(sessionPath);
|
|
2215
|
-
this.agent.sessionId = this.sessionManager.getSessionId();
|
|
2216
|
-
// Reload messages
|
|
2217
|
-
const sessionContext = this.sessionManager.buildSessionContext();
|
|
2218
|
-
// Emit session_switch event to extensions
|
|
2219
|
-
if (this._extensionRunner) {
|
|
2220
|
-
await this._extensionRunner.emit({
|
|
2221
|
-
type: "session_switch",
|
|
2222
|
-
reason: "resume",
|
|
2223
|
-
previousSessionFile,
|
|
2224
|
-
});
|
|
2225
|
-
}
|
|
2226
|
-
// Emit session event to custom tools
|
|
2227
|
-
this.agent.state.messages = sessionContext.messages;
|
|
2228
|
-
// Restore model if saved
|
|
2229
|
-
if (sessionContext.model) {
|
|
2230
|
-
const previousModel = this.model;
|
|
2231
|
-
const availableModels = await this._modelRegistry.getAvailable();
|
|
2232
|
-
const match = availableModels.find((m) => m.provider === sessionContext.model.provider && m.id === sessionContext.model.modelId);
|
|
2233
|
-
if (match) {
|
|
2234
|
-
this.agent.state.model = match;
|
|
2235
|
-
await this._emitModelSelect(match, previousModel, "restore");
|
|
2236
|
-
}
|
|
2237
|
-
}
|
|
2238
|
-
const hasThinkingEntry = this.sessionManager.getBranch().some((entry) => entry.type === "thinking_level_change");
|
|
2239
|
-
const defaultThinkingLevel = this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL;
|
|
2240
|
-
if (hasThinkingEntry) {
|
|
2241
|
-
// Restore thinking level if saved (setThinkingLevel clamps to model capabilities)
|
|
2242
|
-
this.setThinkingLevel(sessionContext.thinkingLevel);
|
|
2243
|
-
}
|
|
2244
|
-
else {
|
|
2245
|
-
const availableLevels = this.getAvailableThinkingLevels();
|
|
2246
|
-
const effectiveLevel = availableLevels.includes(defaultThinkingLevel)
|
|
2247
|
-
? defaultThinkingLevel
|
|
2248
|
-
: this._clampThinkingLevel(defaultThinkingLevel, availableLevels);
|
|
2249
|
-
this.agent.state.thinkingLevel = effectiveLevel;
|
|
2250
|
-
this.sessionManager.appendThinkingLevelChange(effectiveLevel);
|
|
2251
|
-
}
|
|
2252
|
-
this._reconnectToAgent();
|
|
2253
|
-
return true;
|
|
2254
|
-
}
|
|
2255
2135
|
/**
|
|
2256
2136
|
* Set a display name for the current session.
|
|
2257
2137
|
*/
|
|
2258
2138
|
setSessionName(name) {
|
|
2259
2139
|
this.sessionManager.appendSessionInfo(name);
|
|
2260
2140
|
}
|
|
2261
|
-
/**
|
|
2262
|
-
* Create a fork from a specific entry.
|
|
2263
|
-
* Emits before_fork/fork session events to extensions.
|
|
2264
|
-
*
|
|
2265
|
-
* @param entryId ID of the entry to fork from
|
|
2266
|
-
* @returns Object with:
|
|
2267
|
-
* - selectedText: The text of the selected user message (for editor pre-fill)
|
|
2268
|
-
* - cancelled: True if an extension cancelled the fork
|
|
2269
|
-
*/
|
|
2270
|
-
async fork(entryId) {
|
|
2271
|
-
const previousSessionFile = this.sessionFile;
|
|
2272
|
-
const selectedEntry = this.sessionManager.getEntry(entryId);
|
|
2273
|
-
if (!selectedEntry || selectedEntry.type !== "message" || selectedEntry.message.role !== "user") {
|
|
2274
|
-
throw new Error("Invalid entry ID for forking");
|
|
2275
|
-
}
|
|
2276
|
-
const selectedText = this._extractUserMessageText(selectedEntry.message.content);
|
|
2277
|
-
let skipConversationRestore = false;
|
|
2278
|
-
// Emit session_before_fork event (can be cancelled)
|
|
2279
|
-
if (this._extensionRunner?.hasHandlers("session_before_fork")) {
|
|
2280
|
-
const result = (await this._extensionRunner.emit({
|
|
2281
|
-
type: "session_before_fork",
|
|
2282
|
-
entryId,
|
|
2283
|
-
}));
|
|
2284
|
-
if (result?.cancel) {
|
|
2285
|
-
return { selectedText, cancelled: true };
|
|
2286
|
-
}
|
|
2287
|
-
skipConversationRestore = result?.skipConversationRestore ?? false;
|
|
2288
|
-
}
|
|
2289
|
-
// Clear pending messages (bound to old session state)
|
|
2290
|
-
this._pendingNextTurnMessages = [];
|
|
2291
|
-
if (!selectedEntry.parentId) {
|
|
2292
|
-
this.sessionManager.newSession({ parentSession: previousSessionFile });
|
|
2293
|
-
}
|
|
2294
|
-
else {
|
|
2295
|
-
this.sessionManager.createBranchedSession(selectedEntry.parentId);
|
|
2296
|
-
}
|
|
2297
|
-
this.agent.sessionId = this.sessionManager.getSessionId();
|
|
2298
|
-
// Reload messages from entries (works for both file and in-memory mode)
|
|
2299
|
-
const sessionContext = this.sessionManager.buildSessionContext();
|
|
2300
|
-
// Emit session_fork event to extensions (after fork completes)
|
|
2301
|
-
if (this._extensionRunner) {
|
|
2302
|
-
await this._extensionRunner.emit({
|
|
2303
|
-
type: "session_fork",
|
|
2304
|
-
previousSessionFile,
|
|
2305
|
-
});
|
|
2306
|
-
}
|
|
2307
|
-
// Emit session event to custom tools (with reason "fork")
|
|
2308
|
-
if (!skipConversationRestore) {
|
|
2309
|
-
this.agent.state.messages = sessionContext.messages;
|
|
2310
|
-
}
|
|
2311
|
-
return { selectedText, cancelled: false };
|
|
2312
|
-
}
|
|
2313
2141
|
// =========================================================================
|
|
2314
2142
|
// Tree Navigation
|
|
2315
2143
|
// =========================================================================
|
|
@@ -2605,6 +2433,7 @@ export class AgentSession {
|
|
|
2605
2433
|
const toolRenderer = createToolHtmlRenderer({
|
|
2606
2434
|
getToolDefinition: (name) => this.getToolDefinition(name),
|
|
2607
2435
|
theme,
|
|
2436
|
+
cwd: this.sessionManager.getCwd(),
|
|
2608
2437
|
});
|
|
2609
2438
|
return await exportSessionToHtml(this.sessionManager, this.state, {
|
|
2610
2439
|
outputPath,
|
|
@@ -2643,29 +2472,6 @@ export class AgentSession {
|
|
|
2643
2472
|
writeFileSync(filePath, `${lines.join("\n")}\n`);
|
|
2644
2473
|
return filePath;
|
|
2645
2474
|
}
|
|
2646
|
-
/**
|
|
2647
|
-
* Import a JSONL session file.
|
|
2648
|
-
* Copies the file into the session directory and switches to it (like /resume).
|
|
2649
|
-
* @param inputPath Path to the JSONL file to import.
|
|
2650
|
-
* @returns true if the session was switched successfully.
|
|
2651
|
-
*/
|
|
2652
|
-
async importFromJsonl(inputPath) {
|
|
2653
|
-
const resolved = resolve(inputPath);
|
|
2654
|
-
if (!existsSync(resolved)) {
|
|
2655
|
-
throw new Error(`File not found: ${resolved}`);
|
|
2656
|
-
}
|
|
2657
|
-
// Copy into the session directory so we don't modify the original
|
|
2658
|
-
const sessionDir = this.sessionManager.getSessionDir();
|
|
2659
|
-
if (!existsSync(sessionDir)) {
|
|
2660
|
-
mkdirSync(sessionDir, { recursive: true });
|
|
2661
|
-
}
|
|
2662
|
-
const destPath = join(sessionDir, basename(resolved));
|
|
2663
|
-
// Avoid overwriting if source and destination are the same file
|
|
2664
|
-
if (resolve(destPath) !== resolved) {
|
|
2665
|
-
copyFileSync(resolved, destPath);
|
|
2666
|
-
}
|
|
2667
|
-
return this.switchSession(destPath);
|
|
2668
|
-
}
|
|
2669
2475
|
// =========================================================================
|
|
2670
2476
|
// Utilities
|
|
2671
2477
|
// =========================================================================
|