@oh-my-pi/pi-coding-agent 13.12.8 → 13.12.9
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 +16 -0
- package/package.json +7 -7
- package/src/capability/mcp.ts +7 -1
- package/src/cli/session-picker.ts +38 -28
- package/src/discovery/builtin.ts +18 -2
- package/src/discovery/mcp-json.ts +12 -2
- package/src/mcp/oauth-flow.ts +91 -1
- package/src/mcp/types.ts +3 -0
- package/src/modes/components/session-selector.ts +113 -13
- package/src/modes/controllers/mcp-command-controller.ts +20 -15
- package/src/modes/controllers/selector-controller.ts +82 -6
- package/src/modes/interactive-mode.ts +4 -0
- package/src/modes/types.ts +1 -0
- package/src/sdk.ts +17 -4
- package/src/session/agent-session.ts +116 -26
- package/src/session/agent-storage.ts +48 -8
- package/src/session/history-storage.ts +44 -3
- package/src/session/session-manager.ts +49 -1
- package/src/session/session-storage.ts +27 -0
- package/src/slash-commands/builtin-registry.ts +14 -2
|
@@ -150,6 +150,13 @@ export interface TtsrInjectionEntry extends SessionEntryBase {
|
|
|
150
150
|
injectedRules: string[];
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
+
/** Persisted MCP discovery selection state for a session branch. */
|
|
154
|
+
export interface MCPToolSelectionEntry extends SessionEntryBase {
|
|
155
|
+
type: "mcp_tool_selection";
|
|
156
|
+
/** MCP tool names selected for visibility in discovery mode. */
|
|
157
|
+
selectedToolNames: string[];
|
|
158
|
+
}
|
|
159
|
+
|
|
153
160
|
/** Session init entry - captures initial context for subagent sessions (debugging/replay). */
|
|
154
161
|
export interface SessionInitEntry extends SessionEntryBase {
|
|
155
162
|
type: "session_init";
|
|
@@ -206,6 +213,7 @@ export type SessionEntry =
|
|
|
206
213
|
| CustomMessageEntry
|
|
207
214
|
| LabelEntry
|
|
208
215
|
| TtsrInjectionEntry
|
|
216
|
+
| MCPToolSelectionEntry
|
|
209
217
|
| SessionInitEntry
|
|
210
218
|
| ModeChangeEntry;
|
|
211
219
|
|
|
@@ -228,6 +236,10 @@ export interface SessionContext {
|
|
|
228
236
|
models: Record<string, string>;
|
|
229
237
|
/** Names of TTSR rules that have been injected this session */
|
|
230
238
|
injectedTtsrRules: string[];
|
|
239
|
+
/** MCP tool names selected through discovery for this session branch. */
|
|
240
|
+
selectedMCPToolNames: string[];
|
|
241
|
+
/** Whether this branch contains an explicit persisted MCP selection entry. */
|
|
242
|
+
hasPersistedMCPToolSelection: boolean;
|
|
231
243
|
/** Active mode (e.g. "plan") or "none" if no special mode is active */
|
|
232
244
|
mode: string;
|
|
233
245
|
/** Mode-specific data from the last mode_change entry */
|
|
@@ -499,6 +511,8 @@ export function buildSessionContext(
|
|
|
499
511
|
serviceTier: undefined,
|
|
500
512
|
models: {},
|
|
501
513
|
injectedTtsrRules: [],
|
|
514
|
+
selectedMCPToolNames: [],
|
|
515
|
+
hasPersistedMCPToolSelection: false,
|
|
502
516
|
mode: "none",
|
|
503
517
|
};
|
|
504
518
|
}
|
|
@@ -517,6 +531,8 @@ export function buildSessionContext(
|
|
|
517
531
|
serviceTier: undefined,
|
|
518
532
|
models: {},
|
|
519
533
|
injectedTtsrRules: [],
|
|
534
|
+
selectedMCPToolNames: [],
|
|
535
|
+
hasPersistedMCPToolSelection: false,
|
|
520
536
|
mode: "none",
|
|
521
537
|
};
|
|
522
538
|
}
|
|
@@ -535,6 +551,8 @@ export function buildSessionContext(
|
|
|
535
551
|
const models: Record<string, string> = {};
|
|
536
552
|
let compaction: CompactionEntry | null = null;
|
|
537
553
|
const injectedTtsrRulesSet = new Set<string>();
|
|
554
|
+
let selectedMCPToolNames: string[] = [];
|
|
555
|
+
let hasPersistedMCPToolSelection = false;
|
|
538
556
|
let mode = "none";
|
|
539
557
|
let modeData: Record<string, unknown> | undefined;
|
|
540
558
|
|
|
@@ -559,6 +577,9 @@ export function buildSessionContext(
|
|
|
559
577
|
for (const ruleName of entry.injectedRules) {
|
|
560
578
|
injectedTtsrRulesSet.add(ruleName);
|
|
561
579
|
}
|
|
580
|
+
} else if (entry.type === "mcp_tool_selection") {
|
|
581
|
+
selectedMCPToolNames = [...entry.selectedToolNames];
|
|
582
|
+
hasPersistedMCPToolSelection = true;
|
|
562
583
|
} else if (entry.type === "mode_change") {
|
|
563
584
|
mode = entry.mode;
|
|
564
585
|
modeData = entry.data;
|
|
@@ -648,7 +669,17 @@ export function buildSessionContext(
|
|
|
648
669
|
}
|
|
649
670
|
}
|
|
650
671
|
|
|
651
|
-
return {
|
|
672
|
+
return {
|
|
673
|
+
messages,
|
|
674
|
+
thinkingLevel,
|
|
675
|
+
serviceTier,
|
|
676
|
+
models,
|
|
677
|
+
injectedTtsrRules,
|
|
678
|
+
selectedMCPToolNames,
|
|
679
|
+
hasPersistedMCPToolSelection,
|
|
680
|
+
mode,
|
|
681
|
+
modeData,
|
|
682
|
+
};
|
|
652
683
|
}
|
|
653
684
|
|
|
654
685
|
/**
|
|
@@ -2116,6 +2147,23 @@ export class SessionManager {
|
|
|
2116
2147
|
// TTSR (Time Traveling Stream Rules)
|
|
2117
2148
|
// =========================================================================
|
|
2118
2149
|
|
|
2150
|
+
/**
|
|
2151
|
+
* Append an MCP tool selection entry recording the discovery-selected MCP tools.
|
|
2152
|
+
* @param selectedToolNames MCP tool names selected for this branch
|
|
2153
|
+
* @returns Entry id
|
|
2154
|
+
*/
|
|
2155
|
+
appendMCPToolSelection(selectedToolNames: string[]): string {
|
|
2156
|
+
const entry: MCPToolSelectionEntry = {
|
|
2157
|
+
type: "mcp_tool_selection",
|
|
2158
|
+
id: generateId(this.#byId),
|
|
2159
|
+
parentId: this.#leafId,
|
|
2160
|
+
timestamp: new Date().toISOString(),
|
|
2161
|
+
selectedToolNames: [...selectedToolNames],
|
|
2162
|
+
};
|
|
2163
|
+
this.#appendEntry(entry);
|
|
2164
|
+
return entry.id;
|
|
2165
|
+
}
|
|
2166
|
+
|
|
2119
2167
|
/**
|
|
2120
2168
|
* Append a TTSR injection entry recording which rules were injected.
|
|
2121
2169
|
* @param ruleNames Names of rules that were injected
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
|
+
import * as fsp from "node:fs/promises";
|
|
2
3
|
import * as path from "node:path";
|
|
3
4
|
import { isEnoent, toError } from "@oh-my-pi/pi-utils";
|
|
4
5
|
|
|
@@ -184,6 +185,32 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
184
185
|
openWriter(path: string, options?: { flags?: "a" | "w"; onError?: (err: Error) => void }): SessionStorageWriter {
|
|
185
186
|
return new FileSessionStorageWriter(path, options);
|
|
186
187
|
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Delete a session file and its artifacts directory.
|
|
191
|
+
* Artifacts are stored in a sibling directory with the same name minus .jsonl extension.
|
|
192
|
+
*/
|
|
193
|
+
async deleteSessionWithArtifacts(sessionPath: string): Promise<void> {
|
|
194
|
+
// Delete the session file itself
|
|
195
|
+
await this.unlink(sessionPath);
|
|
196
|
+
|
|
197
|
+
// Compute artifacts directory: /path/to/session.jsonl -> /path/to/session
|
|
198
|
+
const artifactsDir = sessionPath.slice(0, -6);
|
|
199
|
+
|
|
200
|
+
// Delete artifacts directory if it exists. Missing directories are fine, but
|
|
201
|
+
// surface real cleanup failures because the session file is already gone.
|
|
202
|
+
try {
|
|
203
|
+
await fsp.rm(artifactsDir, { recursive: true, force: true });
|
|
204
|
+
} catch (err) {
|
|
205
|
+
const error = toError(err);
|
|
206
|
+
throw new Error(
|
|
207
|
+
`Session file deleted but failed to remove artifacts directory ${artifactsDir}: ${error.message}`,
|
|
208
|
+
{
|
|
209
|
+
cause: error,
|
|
210
|
+
},
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
187
214
|
}
|
|
188
215
|
|
|
189
216
|
function matchesPattern(name: string, pattern: string): boolean {
|
|
@@ -229,8 +229,20 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<BuiltinSlashCommandSpec> = [
|
|
|
229
229
|
},
|
|
230
230
|
{
|
|
231
231
|
name: "session",
|
|
232
|
-
description: "
|
|
233
|
-
|
|
232
|
+
description: "Session management commands",
|
|
233
|
+
subcommands: [
|
|
234
|
+
{ name: "info", description: "Show session info and stats" },
|
|
235
|
+
{ name: "delete", description: "Delete current session and return to selector" },
|
|
236
|
+
],
|
|
237
|
+
allowArgs: true,
|
|
238
|
+
handle: async (command, runtime) => {
|
|
239
|
+
const sub = command.args.trim().toLowerCase() || "info";
|
|
240
|
+
if (sub === "delete") {
|
|
241
|
+
runtime.ctx.editor.setText("");
|
|
242
|
+
await runtime.ctx.handleSessionDeleteCommand();
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
// Default: show session info
|
|
234
246
|
await runtime.ctx.handleSessionCommand();
|
|
235
247
|
runtime.ctx.editor.setText("");
|
|
236
248
|
},
|