@oh-my-pi/pi-coding-agent 12.17.0 → 12.18.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/CHANGELOG.md +42 -0
- package/package.json +414 -92
- package/src/capability/index.ts +5 -8
- package/src/cli/stats-cli.ts +3 -3
- package/src/exec/bash-executor.ts +92 -58
- package/src/ipy/executor.ts +42 -32
- package/src/ipy/gateway-coordinator.ts +5 -8
- package/src/ipy/kernel.ts +28 -47
- package/src/main.ts +56 -61
- package/src/mcp/config.ts +102 -5
- package/src/mcp/index.ts +3 -1
- package/src/mcp/loader.ts +3 -0
- package/src/mcp/manager.ts +3 -0
- package/src/modes/controllers/extension-ui-controller.ts +28 -6
- package/src/modes/interactive-mode.ts +15 -18
- package/src/modes/types.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +7 -0
- package/src/sdk.ts +97 -115
- package/src/system-prompt.ts +15 -36
- package/src/tools/bash-interactive.ts +46 -42
- package/src/tools/bash.ts +6 -7
- package/src/tools/gemini-image.ts +2 -11
- package/src/tools/index.ts +18 -27
- package/src/utils/shell-snapshot.ts +18 -3
- package/src/web/search/providers/gemini.ts +2 -24
- package/src/utils/timings.ts +0 -26
package/src/sdk.ts
CHANGED
|
@@ -83,10 +83,6 @@ import {
|
|
|
83
83
|
import { ToolContextStore } from "./tools/context";
|
|
84
84
|
import { getGeminiImageTools } from "./tools/gemini-image";
|
|
85
85
|
import { EventBus } from "./utils/event-bus";
|
|
86
|
-
import { time } from "./utils/timings";
|
|
87
|
-
|
|
88
|
-
/** Conditional startup debug prints (stderr) when PI_DEBUG_STARTUP is set */
|
|
89
|
-
const debugStartup = $env.PI_DEBUG_STARTUP ? (stage: string) => process.stderr.write(`[startup] ${stage}\n`) : () => {};
|
|
90
86
|
|
|
91
87
|
// Types
|
|
92
88
|
export interface CreateAgentSessionOptions {
|
|
@@ -540,7 +536,6 @@ function createCustomToolsExtension(tools: CustomTool[]): ExtensionFactory {
|
|
|
540
536
|
* ```
|
|
541
537
|
*/
|
|
542
538
|
export async function createAgentSession(options: CreateAgentSessionOptions = {}): Promise<CreateAgentSessionResult> {
|
|
543
|
-
debugStartup("sdk:createAgentSession:entry");
|
|
544
539
|
const cwd = options.cwd ?? getProjectDir();
|
|
545
540
|
const agentDir = options.agentDir ?? getDefaultAgentDir();
|
|
546
541
|
const eventBus = options.eventBus ?? new EventBus();
|
|
@@ -549,17 +544,20 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
549
544
|
registerPythonCleanup();
|
|
550
545
|
|
|
551
546
|
// Use provided or create AuthStorage and ModelRegistry
|
|
552
|
-
const authStorage =
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
547
|
+
const { authStorage, modelRegistry } = await logger.timeAsync("discoverModels", async () => {
|
|
548
|
+
const authStorage = options.authStorage ?? (await discoverAuthStorage(agentDir));
|
|
549
|
+
const modelRegistry = options.modelRegistry ?? new ModelRegistry(authStorage);
|
|
550
|
+
if (!options.modelRegistry) {
|
|
551
|
+
await modelRegistry.refresh();
|
|
552
|
+
}
|
|
553
|
+
return { authStorage, modelRegistry };
|
|
554
|
+
});
|
|
558
555
|
|
|
559
|
-
const settings =
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
556
|
+
const settings = await logger.timeAsync(
|
|
557
|
+
"settings",
|
|
558
|
+
async () => options.settings ?? (await Settings.init({ cwd, agentDir })),
|
|
559
|
+
);
|
|
560
|
+
logger.time("initializeWithSettings", initializeWithSettings, settings);
|
|
563
561
|
const skillsSettings = settings.getGroup("skills") as SkillsSettings;
|
|
564
562
|
const discoveredSkillsPromise =
|
|
565
563
|
options.skills === undefined ? discoverSkills(cwd, agentDir, skillsSettings) : undefined;
|
|
@@ -568,8 +566,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
568
566
|
setPreferredSearchProvider(settings.get("providers.webSearch") ?? "auto");
|
|
569
567
|
setPreferredImageProvider(settings.get("providers.image") ?? "auto");
|
|
570
568
|
|
|
571
|
-
const sessionManager = options.sessionManager ?? SessionManager.create
|
|
572
|
-
time("sessionManager");
|
|
569
|
+
const sessionManager = options.sessionManager ?? logger.time("sessionManager", SessionManager.create, cwd);
|
|
573
570
|
const sessionId = sessionManager.getSessionId();
|
|
574
571
|
const modelApiKeyAvailability = new Map<string, boolean>();
|
|
575
572
|
const getModelAvailabilityKey = (candidate: Model): string =>
|
|
@@ -587,8 +584,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
587
584
|
};
|
|
588
585
|
|
|
589
586
|
// Check if session has existing data to restore
|
|
590
|
-
const existingSession = sessionManager.buildSessionContext();
|
|
591
|
-
time("loadSession");
|
|
587
|
+
const existingSession = logger.time("loadSession", () => sessionManager.buildSessionContext());
|
|
592
588
|
const hasExistingSession = existingSession.messages.length > 0;
|
|
593
589
|
const hasThinkingEntry = sessionManager.getBranch().some(entry => entry.type === "thinking_level_change");
|
|
594
590
|
|
|
@@ -667,46 +663,52 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
667
663
|
skills = options.skills;
|
|
668
664
|
skillWarnings = [];
|
|
669
665
|
} else {
|
|
670
|
-
const discovered =
|
|
671
|
-
|
|
666
|
+
const discovered = await logger.timeAsync("discoverSkills", async () =>
|
|
667
|
+
discoveredSkillsPromise ? await discoveredSkillsPromise : { skills: [], warnings: [] },
|
|
668
|
+
);
|
|
672
669
|
skills = discovered.skills;
|
|
673
670
|
skillWarnings = discovered.warnings;
|
|
674
671
|
}
|
|
675
672
|
|
|
676
|
-
debugStartup("sdk:discoverSkills");
|
|
677
|
-
|
|
678
673
|
// Discover rules
|
|
679
|
-
const
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
674
|
+
const { ttsrManager, rulesResult, registeredTtsrRuleNames } = await logger.timeAsync(
|
|
675
|
+
"discoverTtsrRules",
|
|
676
|
+
async () => {
|
|
677
|
+
const ttsrSettings = settings.getGroup("ttsr");
|
|
678
|
+
const ttsrManager = new TtsrManager(ttsrSettings);
|
|
679
|
+
const rulesResult =
|
|
680
|
+
options.rules !== undefined
|
|
681
|
+
? { items: options.rules, warnings: undefined }
|
|
682
|
+
: await loadCapability<Rule>(ruleCapability.id, { cwd });
|
|
683
|
+
const registeredTtsrRuleNames = new Set<string>();
|
|
684
|
+
for (const rule of rulesResult.items) {
|
|
685
|
+
if (rule.condition && rule.condition.length > 0) {
|
|
686
|
+
if (ttsrManager.addRule(rule)) {
|
|
687
|
+
registeredTtsrRuleNames.add(rule.name);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
690
|
}
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
691
|
+
if (existingSession.injectedTtsrRules.length > 0) {
|
|
692
|
+
ttsrManager.restoreInjected(existingSession.injectedTtsrRules);
|
|
693
|
+
}
|
|
694
|
+
return { ttsrManager, rulesResult, registeredTtsrRuleNames };
|
|
695
|
+
},
|
|
696
|
+
);
|
|
697
697
|
|
|
698
698
|
// Filter rules for the rulebook (non-TTSR, non-alwaysApply, with descriptions)
|
|
699
|
-
const rulebookRules =
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
699
|
+
const rulebookRules = logger.time("filterRulebookRules", () =>
|
|
700
|
+
rulesResult.items.filter((rule: Rule) => {
|
|
701
|
+
if (registeredTtsrRuleNames.has(rule.name)) return false;
|
|
702
|
+
if (rule.alwaysApply) return false;
|
|
703
|
+
if (!rule.description) return false;
|
|
704
|
+
return true;
|
|
705
|
+
}),
|
|
706
|
+
);
|
|
706
707
|
|
|
707
|
-
const contextFiles =
|
|
708
|
-
|
|
709
|
-
|
|
708
|
+
const contextFiles = await logger.timeAsync(
|
|
709
|
+
"discoverContextFiles",
|
|
710
|
+
async () => options.contextFiles ?? (await discoverContextFiles(cwd, agentDir)),
|
|
711
|
+
);
|
|
710
712
|
|
|
711
713
|
let agent: Agent;
|
|
712
714
|
let session: AgentSession;
|
|
@@ -782,35 +784,30 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
782
784
|
options.parentTaskPrefix ? { parentPrefix: options.parentTaskPrefix } : undefined,
|
|
783
785
|
);
|
|
784
786
|
|
|
785
|
-
debugStartup("sdk:createTools:start");
|
|
786
787
|
// Create built-in tools (already wrapped with meta notice formatting)
|
|
787
|
-
const builtinTools = await createTools(toolSession, options.toolNames);
|
|
788
|
-
debugStartup("sdk:createTools");
|
|
789
|
-
time("createAllTools");
|
|
788
|
+
const builtinTools = await logger.timeAsync("createAllTools", () => createTools(toolSession, options.toolNames));
|
|
790
789
|
|
|
791
|
-
debugStartup("sdk:discoverMCP:start");
|
|
792
790
|
// Discover MCP tools from .mcp.json files
|
|
793
791
|
let mcpManager: MCPManager | undefined;
|
|
794
792
|
const enableMCP = options.enableMCP ?? true;
|
|
795
793
|
const customTools: CustomTool[] = [];
|
|
796
794
|
if (enableMCP) {
|
|
797
|
-
const mcpResult = await discoverAndLoadMCPTools
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
chalk.gray(`Connecting to MCP servers: ${serverNames.join(", ")}
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
debugStartup("sdk:discoverAndLoadMCPTools");
|
|
795
|
+
const mcpResult = await logger.timeAsync("discoverAndLoadMCPTools", () =>
|
|
796
|
+
discoverAndLoadMCPTools(cwd, {
|
|
797
|
+
onConnecting: serverNames => {
|
|
798
|
+
if (options.hasUI && serverNames.length > 0) {
|
|
799
|
+
process.stderr.write(`${chalk.gray(`Connecting to MCP servers: ${serverNames.join(", ")}…`)}\n`);
|
|
800
|
+
}
|
|
801
|
+
},
|
|
802
|
+
enableProjectConfig: settings.get("mcp.enableProjectConfig") ?? true,
|
|
803
|
+
// Always filter Exa - we have native integration
|
|
804
|
+
filterExa: true,
|
|
805
|
+
// Filter browser MCP servers when builtin browser tool is active
|
|
806
|
+
filterBrowser: (settings.get("browser.enabled") as boolean) ?? false,
|
|
807
|
+
cacheStorage: settings.getStorage(),
|
|
808
|
+
authStorage,
|
|
809
|
+
}),
|
|
810
|
+
);
|
|
814
811
|
mcpManager = mcpResult.manager;
|
|
815
812
|
toolSession.mcpManager = mcpManager;
|
|
816
813
|
|
|
@@ -830,18 +827,16 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
830
827
|
}
|
|
831
828
|
}
|
|
832
829
|
|
|
833
|
-
debugStartup("sdk:geminiImageTools:start");
|
|
834
830
|
// Add Gemini image tools if GEMINI_API_KEY (or GOOGLE_API_KEY) is available
|
|
835
|
-
const geminiImageTools = await getGeminiImageTools
|
|
831
|
+
const geminiImageTools = await logger.timeAsync("getGeminiImageTools", getGeminiImageTools);
|
|
836
832
|
if (geminiImageTools.length > 0) {
|
|
837
833
|
customTools.push(...(geminiImageTools as unknown as CustomTool[]));
|
|
838
834
|
}
|
|
839
|
-
time("getGeminiImageTools");
|
|
840
835
|
|
|
841
836
|
// Add specialized Exa web search tools if EXA_API_KEY is available
|
|
842
837
|
const exaSettings = settings.getGroup("exa");
|
|
843
838
|
if (exaSettings.enabled && exaSettings.enableSearch) {
|
|
844
|
-
const exaSearchTools = await getSearchTools
|
|
839
|
+
const exaSearchTools = await logger.timeAsync("getSearchTools", getSearchTools, {
|
|
845
840
|
enableLinkedin: exaSettings.enableLinkedin as boolean,
|
|
846
841
|
enableCompany: exaSettings.enableCompany as boolean,
|
|
847
842
|
});
|
|
@@ -850,34 +845,34 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
850
845
|
if (specializedTools.length > 0) {
|
|
851
846
|
customTools.push(...specializedTools);
|
|
852
847
|
}
|
|
853
|
-
time("getSearchTools");
|
|
854
848
|
}
|
|
855
849
|
|
|
856
|
-
debugStartup("sdk:discoverCustomTools:start");
|
|
857
850
|
// Discover and load custom tools from .omp/tools/, .claude/tools/, etc.
|
|
858
851
|
const builtInToolNames = builtinTools.map(t => t.name);
|
|
859
|
-
const discoveredCustomTools = await
|
|
852
|
+
const discoveredCustomTools = await logger.timeAsync(
|
|
853
|
+
"discoverAndLoadCustomTools",
|
|
854
|
+
discoverAndLoadCustomTools,
|
|
855
|
+
[],
|
|
856
|
+
cwd,
|
|
857
|
+
builtInToolNames,
|
|
858
|
+
);
|
|
860
859
|
for (const { path, error } of discoveredCustomTools.errors) {
|
|
861
860
|
logger.error("Custom tool load failed", { path, error });
|
|
862
861
|
}
|
|
863
862
|
if (discoveredCustomTools.tools.length > 0) {
|
|
864
863
|
customTools.push(...discoveredCustomTools.tools.map(loaded => loaded.tool));
|
|
865
864
|
}
|
|
866
|
-
time("discoverAndLoadCustomTools");
|
|
867
|
-
debugStartup("sdk:discoverCustomTools:done");
|
|
868
865
|
|
|
869
866
|
const inlineExtensions: ExtensionFactory[] = options.extensions ? [...options.extensions] : [];
|
|
870
867
|
if (customTools.length > 0) {
|
|
871
868
|
inlineExtensions.push(createCustomToolsExtension(customTools));
|
|
872
869
|
}
|
|
873
870
|
|
|
874
|
-
debugStartup("sdk:loadExtensions:start");
|
|
875
871
|
// Load extensions (discovers from standard locations + configured paths)
|
|
876
872
|
let extensionsResult: LoadExtensionsResult;
|
|
877
873
|
if (options.disableExtensionDiscovery) {
|
|
878
874
|
const configuredPaths = options.additionalExtensionPaths ?? [];
|
|
879
|
-
extensionsResult = await loadExtensions
|
|
880
|
-
time("loadExtensions");
|
|
875
|
+
extensionsResult = await logger.timeAsync("loadExtensions", loadExtensions, configuredPaths, cwd, eventBus);
|
|
881
876
|
for (const { path, error } of extensionsResult.errors) {
|
|
882
877
|
logger.error("Failed to load extension", { path, error });
|
|
883
878
|
}
|
|
@@ -889,14 +884,13 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
889
884
|
...(options.additionalExtensionPaths ?? []),
|
|
890
885
|
...((settings.get("extensions") as string[]) ?? []),
|
|
891
886
|
];
|
|
892
|
-
extensionsResult = await
|
|
887
|
+
extensionsResult = await logger.timeAsync(
|
|
888
|
+
"discoverAndLoadExtensions",
|
|
889
|
+
discoverAndLoadExtensions,
|
|
893
890
|
configuredPaths,
|
|
894
891
|
cwd,
|
|
895
892
|
eventBus,
|
|
896
|
-
(settings.get("disabledExtensions") as string[]) ?? [],
|
|
897
893
|
);
|
|
898
|
-
time("discoverAndLoadExtensions");
|
|
899
|
-
debugStartup("sdk:discoverAndLoadExtensions");
|
|
900
894
|
for (const { path, error } of extensionsResult.errors) {
|
|
901
895
|
logger.error("Failed to load extension", { path, error });
|
|
902
896
|
}
|
|
@@ -957,7 +951,6 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
957
951
|
break;
|
|
958
952
|
}
|
|
959
953
|
}
|
|
960
|
-
time("findAvailableModel");
|
|
961
954
|
if (model) {
|
|
962
955
|
if (modelFallbackMessage) {
|
|
963
956
|
modelFallbackMessage += `. Using ${model.provider}/${model.id}`;
|
|
@@ -968,13 +961,11 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
968
961
|
}
|
|
969
962
|
}
|
|
970
963
|
|
|
971
|
-
time("findModel");
|
|
972
964
|
// Discover custom commands (TypeScript slash commands)
|
|
973
965
|
const customCommandsResult: CustomCommandsLoadResult = options.disableExtensionDiscovery
|
|
974
966
|
? { commands: [], errors: [] }
|
|
975
|
-
: await loadCustomCommandsInternal
|
|
967
|
+
: await logger.timeAsync("discoverCustomCommands", loadCustomCommandsInternal, { cwd, agentDir });
|
|
976
968
|
if (!options.disableExtensionDiscovery) {
|
|
977
|
-
time("discoverCustomCommands");
|
|
978
969
|
for (const { path, error } of customCommandsResult.errors) {
|
|
979
970
|
logger.error("Failed to load custom command", { path, error });
|
|
980
971
|
}
|
|
@@ -1048,7 +1039,6 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1048
1039
|
if (model?.provider === "cursor") {
|
|
1049
1040
|
toolRegistry.delete("edit");
|
|
1050
1041
|
}
|
|
1051
|
-
time("combineTools");
|
|
1052
1042
|
|
|
1053
1043
|
let cursorEventEmitter: ((event: AgentEvent) => void) | undefined;
|
|
1054
1044
|
const cursorExecHandlers = new CursorExecHandlers({
|
|
@@ -1115,16 +1105,20 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1115
1105
|
}
|
|
1116
1106
|
}
|
|
1117
1107
|
|
|
1118
|
-
const systemPrompt = await
|
|
1119
|
-
|
|
1120
|
-
|
|
1108
|
+
const systemPrompt = await logger.timeAsync(
|
|
1109
|
+
"buildSystemPrompt",
|
|
1110
|
+
rebuildSystemPrompt,
|
|
1111
|
+
initialToolNames,
|
|
1112
|
+
toolRegistry,
|
|
1113
|
+
);
|
|
1121
1114
|
|
|
1122
|
-
const promptTemplates =
|
|
1123
|
-
|
|
1115
|
+
const promptTemplates =
|
|
1116
|
+
options.promptTemplates ??
|
|
1117
|
+
(await logger.timeAsync("discoverPromptTemplates", discoverPromptTemplates, cwd, agentDir));
|
|
1124
1118
|
toolSession.promptTemplates = promptTemplates;
|
|
1125
1119
|
|
|
1126
|
-
const slashCommands =
|
|
1127
|
-
|
|
1120
|
+
const slashCommands =
|
|
1121
|
+
options.slashCommands ?? (await logger.timeAsync("discoverSlashCommands", discoverSlashCommands, cwd));
|
|
1128
1122
|
|
|
1129
1123
|
// Create convertToLlm wrapper that filters images if blockImages is enabled (defense-in-depth)
|
|
1130
1124
|
const convertToLlmWithBlockImages = (messages: AgentMessage[]): Message[] => {
|
|
@@ -1164,13 +1158,12 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1164
1158
|
// Load and create secret obfuscator if secrets are enabled
|
|
1165
1159
|
let obfuscator: SecretObfuscator | undefined;
|
|
1166
1160
|
if (settings.get("secrets.enabled")) {
|
|
1167
|
-
const fileEntries = await loadSecrets
|
|
1161
|
+
const fileEntries = await logger.timeAsync("loadSecrets", loadSecrets, cwd, agentDir);
|
|
1168
1162
|
const envEntries = collectEnvSecrets();
|
|
1169
1163
|
const allEntries = [...envEntries, ...fileEntries];
|
|
1170
1164
|
if (allEntries.length > 0) {
|
|
1171
1165
|
obfuscator = new SecretObfuscator(allEntries);
|
|
1172
1166
|
}
|
|
1173
|
-
time("loadSecrets");
|
|
1174
1167
|
}
|
|
1175
1168
|
|
|
1176
1169
|
// Final convertToLlm: chain block-images filter with secret obfuscation
|
|
@@ -1228,8 +1221,6 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1228
1221
|
intentTracing: settings.get("tools.intentTracing") || $env.PI_INTENT_TRACING === "1",
|
|
1229
1222
|
});
|
|
1230
1223
|
cursorEventEmitter = event => agent.emitExternalEvent(event);
|
|
1231
|
-
debugStartup("sdk:createAgent");
|
|
1232
|
-
time("createAgent");
|
|
1233
1224
|
|
|
1234
1225
|
// Restore messages if session has existing data
|
|
1235
1226
|
if (hasExistingSession) {
|
|
@@ -1264,20 +1255,15 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1264
1255
|
forceCopilotAgentInitiator,
|
|
1265
1256
|
obfuscator,
|
|
1266
1257
|
});
|
|
1267
|
-
debugStartup("sdk:createAgentSession");
|
|
1268
|
-
time("createAgentSession");
|
|
1269
1258
|
|
|
1270
1259
|
if (model?.api === "openai-codex-responses") {
|
|
1271
1260
|
try {
|
|
1272
|
-
|
|
1273
|
-
await prewarmOpenAICodexResponses(model, {
|
|
1261
|
+
await logger.timeAsync("prewarmCodexWebsocket", prewarmOpenAICodexResponses, model, {
|
|
1274
1262
|
apiKey: await modelRegistry.getApiKey(model, sessionId),
|
|
1275
1263
|
sessionId,
|
|
1276
1264
|
preferWebsockets: preferOpenAICodexWebsockets,
|
|
1277
1265
|
providerSessionState: session.providerSessionState,
|
|
1278
1266
|
});
|
|
1279
|
-
debugStartup("sdk:prewarmCodexWebsocket:done");
|
|
1280
|
-
time("prewarmCodexWebsocket");
|
|
1281
1267
|
} catch (error) {
|
|
1282
1268
|
logger.debug("Codex websocket prewarm failed", {
|
|
1283
1269
|
error: error instanceof Error ? error.message : String(error),
|
|
@@ -1291,17 +1277,14 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1291
1277
|
let lspServers: CreateAgentSessionResult["lspServers"];
|
|
1292
1278
|
if (enableLsp && settings.get("lsp.diagnosticsOnWrite")) {
|
|
1293
1279
|
try {
|
|
1294
|
-
|
|
1295
|
-
const result = await warmupLspServers(cwd, {
|
|
1280
|
+
const result = await logger.timeAsync("warmupLspServers", warmupLspServers, cwd, {
|
|
1296
1281
|
onConnecting: serverNames => {
|
|
1297
1282
|
if (options.hasUI && serverNames.length > 0) {
|
|
1298
1283
|
process.stderr.write(chalk.gray(`Starting LSP servers: ${serverNames.join(", ")}…\n`));
|
|
1299
1284
|
}
|
|
1300
1285
|
},
|
|
1301
1286
|
});
|
|
1302
|
-
debugStartup("sdk:warmupLspServers:done");
|
|
1303
1287
|
lspServers = result.servers;
|
|
1304
|
-
time("warmupLspServers");
|
|
1305
1288
|
} catch (error) {
|
|
1306
1289
|
logger.warn("LSP server warmup failed", { cwd, error: String(error) });
|
|
1307
1290
|
}
|
|
@@ -1315,7 +1298,6 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1315
1298
|
taskDepth,
|
|
1316
1299
|
});
|
|
1317
1300
|
|
|
1318
|
-
debugStartup("sdk:return");
|
|
1319
1301
|
return {
|
|
1320
1302
|
session,
|
|
1321
1303
|
extensionsResult,
|
package/src/system-prompt.ts
CHANGED
|
@@ -18,9 +18,6 @@ import customSystemPromptTemplate from "./prompts/system/custom-system-prompt.md
|
|
|
18
18
|
import systemPromptTemplate from "./prompts/system/system-prompt.md" with { type: "text" };
|
|
19
19
|
import type { ToolName } from "./tools";
|
|
20
20
|
|
|
21
|
-
/** Conditional startup debug prints (stderr) when PI_DEBUG_STARTUP is set */
|
|
22
|
-
const debugStartup = $env.PI_DEBUG_STARTUP ? (stage: string) => process.stderr.write(`[startup] ${stage}\n`) : () => {};
|
|
23
|
-
|
|
24
21
|
interface GitContext {
|
|
25
22
|
isRepo: boolean;
|
|
26
23
|
currentBranch: string;
|
|
@@ -61,6 +58,7 @@ export async function loadGitContext(cwd: string): Promise<GitContext | null> {
|
|
|
61
58
|
stdout: "pipe",
|
|
62
59
|
stderr: "ignore",
|
|
63
60
|
timeout: timeout,
|
|
61
|
+
killSignal: "SIGKILL",
|
|
64
62
|
});
|
|
65
63
|
return untilAborted(abortSignal, async () => {
|
|
66
64
|
const exitCode = await proc.exited;
|
|
@@ -354,21 +352,15 @@ async function saveGpuCache(info: GpuCache): Promise<void> {
|
|
|
354
352
|
}
|
|
355
353
|
|
|
356
354
|
async function getCachedGpu(): Promise<string | undefined> {
|
|
357
|
-
|
|
358
|
-
const cached = await loadGpuCache();
|
|
355
|
+
const cached = await logger.timeAsync("getCachedGpu:loadGpuCache", loadGpuCache);
|
|
359
356
|
if (cached) return cached.gpu;
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
debugStartup("system-prompt:getEnvironmentInfo:saveGpuCache");
|
|
363
|
-
if (gpu) await saveGpuCache({ gpu });
|
|
357
|
+
const gpu = await logger.timeAsync("getCachedGpu:getGpuModel", getGpuModel);
|
|
358
|
+
if (gpu) await logger.timeAsync("getCachedGpu:saveGpuCache", saveGpuCache, { gpu });
|
|
364
359
|
return gpu ?? undefined;
|
|
365
360
|
}
|
|
366
361
|
async function getEnvironmentInfo(): Promise<Array<{ label: string; value: string }>> {
|
|
367
|
-
|
|
368
|
-
const gpu = await getCachedGpu();
|
|
369
|
-
debugStartup("system-prompt:getEnvironmentInfo:getCpuInfo");
|
|
362
|
+
const gpu = await logger.timeAsync("getEnvironmentInfo:getCachedGpu", getCachedGpu);
|
|
370
363
|
const cpus = os.cpus();
|
|
371
|
-
debugStartup("system-prompt:getEnvironmentInfo:buildEntries");
|
|
372
364
|
const entries: Array<{ label: string; value: string | undefined }> = [
|
|
373
365
|
{ label: "OS", value: `${os.platform()} ${os.release()}` },
|
|
374
366
|
{ label: "Distro", value: os.type() },
|
|
@@ -380,7 +372,6 @@ async function getEnvironmentInfo(): Promise<Array<{ label: string; value: strin
|
|
|
380
372
|
{ label: "DE", value: getDesktopEnvironment() },
|
|
381
373
|
{ label: "WM", value: getWindowManager() },
|
|
382
374
|
];
|
|
383
|
-
debugStartup("system-prompt:getEnvironmentInfo:done");
|
|
384
375
|
return entries.filter((e): e is { label: string; value: string } => e.value != null && e.value !== "unknown");
|
|
385
376
|
}
|
|
386
377
|
|
|
@@ -511,33 +502,23 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
511
502
|
const preloadedSkills = providedPreloadedSkills;
|
|
512
503
|
|
|
513
504
|
const prepPromise = (async () => {
|
|
514
|
-
const systemPromptCustomizationPromise = (
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
return customization;
|
|
518
|
-
})();
|
|
505
|
+
const systemPromptCustomizationPromise = logger.timeAsync("loadSystemPromptFiles", loadSystemPromptFiles, {
|
|
506
|
+
cwd: resolvedCwd,
|
|
507
|
+
});
|
|
519
508
|
const contextFilesPromise = providedContextFiles
|
|
520
509
|
? Promise.resolve(providedContextFiles)
|
|
521
|
-
: loadProjectContextFiles
|
|
522
|
-
const agentsMdSearchPromise = buildAgentsMdSearch
|
|
510
|
+
: logger.timeAsync("loadProjectContextFiles", loadProjectContextFiles, { cwd: resolvedCwd });
|
|
511
|
+
const agentsMdSearchPromise = logger.timeAsync("buildAgentsMdSearch", buildAgentsMdSearch, resolvedCwd);
|
|
523
512
|
const skillsPromise: Promise<Skill[]> =
|
|
524
513
|
providedSkills !== undefined
|
|
525
514
|
? Promise.resolve(providedSkills)
|
|
526
515
|
: skillsSettings?.enabled !== false
|
|
527
516
|
? loadSkills({ ...skillsSettings, cwd: resolvedCwd }).then(result => result.skills)
|
|
528
517
|
: Promise.resolve([]);
|
|
529
|
-
const preloadedSkillContentsPromise =
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
return loaded;
|
|
534
|
-
})();
|
|
535
|
-
const gitPromise = (async () => {
|
|
536
|
-
debugStartup("system-prompt:loadGitContext:start");
|
|
537
|
-
const loaded = await loadGitContext(resolvedCwd);
|
|
538
|
-
debugStartup("system-prompt:loadGitContext:done");
|
|
539
|
-
return loaded;
|
|
540
|
-
})();
|
|
518
|
+
const preloadedSkillContentsPromise = preloadedSkills
|
|
519
|
+
? await logger.timeAsync("loadPreloadedSkills", loadPreloadedSkillContents, preloadedSkills)
|
|
520
|
+
: [];
|
|
521
|
+
const gitPromise = logger.timeAsync("loadGitContext", loadGitContext, resolvedCwd);
|
|
541
522
|
|
|
542
523
|
const [
|
|
543
524
|
resolvedCustomPrompt,
|
|
@@ -677,9 +658,7 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
677
658
|
});
|
|
678
659
|
}
|
|
679
660
|
|
|
680
|
-
|
|
681
|
-
const environment = await getEnvironmentInfo();
|
|
682
|
-
debugStartup("system-prompt:getEnvironmentInfo:done");
|
|
661
|
+
const environment = await logger.timeAsync("getEnvironmentInfo", getEnvironmentInfo);
|
|
683
662
|
return renderPromptTemplate(systemPromptTemplate, {
|
|
684
663
|
tools: toolNamesArray,
|
|
685
664
|
toolDescriptions,
|
|
@@ -276,6 +276,50 @@ class BashInteractiveOverlayComponent implements Component {
|
|
|
276
276
|
}
|
|
277
277
|
}
|
|
278
278
|
|
|
279
|
+
const NO_PAGER_ENV = {
|
|
280
|
+
// Disable pagers so commands don't block on interactive views.
|
|
281
|
+
PAGER: "cat",
|
|
282
|
+
GIT_PAGER: "cat",
|
|
283
|
+
MANPAGER: "cat",
|
|
284
|
+
SYSTEMD_PAGER: "cat",
|
|
285
|
+
BAT_PAGER: "cat",
|
|
286
|
+
DELTA_PAGER: "cat",
|
|
287
|
+
GH_PAGER: "cat",
|
|
288
|
+
GLAB_PAGER: "cat",
|
|
289
|
+
PSQL_PAGER: "cat",
|
|
290
|
+
MYSQL_PAGER: "cat",
|
|
291
|
+
AWS_PAGER: "",
|
|
292
|
+
HOMEBREW_PAGER: "cat",
|
|
293
|
+
LESS: "FRX",
|
|
294
|
+
// Disable editor and terminal credential prompts.
|
|
295
|
+
GIT_EDITOR: "true",
|
|
296
|
+
VISUAL: "true",
|
|
297
|
+
EDITOR: "true",
|
|
298
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
299
|
+
SSH_ASKPASS: "/usr/bin/false",
|
|
300
|
+
CI: "1",
|
|
301
|
+
// Package manager defaults for unattended execution.
|
|
302
|
+
npm_config_yes: "true",
|
|
303
|
+
npm_config_update_notifier: "false",
|
|
304
|
+
npm_config_fund: "false",
|
|
305
|
+
npm_config_audit: "false",
|
|
306
|
+
npm_config_progress: "false",
|
|
307
|
+
PNPM_DISABLE_SELF_UPDATE_CHECK: "true",
|
|
308
|
+
PNPM_UPDATE_NOTIFIER: "false",
|
|
309
|
+
YARN_ENABLE_TELEMETRY: "0",
|
|
310
|
+
YARN_ENABLE_PROGRESS_BARS: "0",
|
|
311
|
+
// Cross-language/tooling non-interactive defaults.
|
|
312
|
+
CARGO_TERM_PROGRESS_WHEN: "never",
|
|
313
|
+
DEBIAN_FRONTEND: "noninteractive",
|
|
314
|
+
PIP_NO_INPUT: "1",
|
|
315
|
+
PIP_DISABLE_PIP_VERSION_CHECK: "1",
|
|
316
|
+
TF_INPUT: "0",
|
|
317
|
+
TF_IN_AUTOMATION: "1",
|
|
318
|
+
GH_PROMPT_DISABLED: "1",
|
|
319
|
+
COMPOSER_NO_INTERACTION: "1",
|
|
320
|
+
CLOUDSDK_CORE_DISABLE_PROMPTS: "1",
|
|
321
|
+
};
|
|
322
|
+
|
|
279
323
|
export async function runInteractiveBashPty(
|
|
280
324
|
ui: NonNullable<AgentToolContext["ui"]>,
|
|
281
325
|
options: {
|
|
@@ -346,54 +390,14 @@ export async function runInteractiveBashPty(
|
|
|
346
390
|
timeoutMs: options.timeoutMs,
|
|
347
391
|
env: {
|
|
348
392
|
...options.env,
|
|
349
|
-
|
|
350
|
-
PAGER: "cat",
|
|
351
|
-
GIT_PAGER: "cat",
|
|
352
|
-
MANPAGER: "cat",
|
|
353
|
-
SYSTEMD_PAGER: "cat",
|
|
354
|
-
BAT_PAGER: "cat",
|
|
355
|
-
DELTA_PAGER: "cat",
|
|
356
|
-
GH_PAGER: "cat",
|
|
357
|
-
GLAB_PAGER: "cat",
|
|
358
|
-
PSQL_PAGER: "cat",
|
|
359
|
-
MYSQL_PAGER: "cat",
|
|
360
|
-
AWS_PAGER: "",
|
|
361
|
-
HOMEBREW_PAGER: "cat",
|
|
362
|
-
LESS: "FRX",
|
|
363
|
-
// Disable editor and terminal credential prompts.
|
|
364
|
-
GIT_EDITOR: "true",
|
|
365
|
-
VISUAL: "true",
|
|
366
|
-
EDITOR: "true",
|
|
367
|
-
GIT_TERMINAL_PROMPT: "0",
|
|
368
|
-
SSH_ASKPASS: "/usr/bin/false",
|
|
369
|
-
CI: "1",
|
|
370
|
-
// Package manager defaults for unattended execution.
|
|
371
|
-
npm_config_yes: "true",
|
|
372
|
-
npm_config_update_notifier: "false",
|
|
373
|
-
npm_config_fund: "false",
|
|
374
|
-
npm_config_audit: "false",
|
|
375
|
-
npm_config_progress: "false",
|
|
376
|
-
PNPM_DISABLE_SELF_UPDATE_CHECK: "true",
|
|
377
|
-
PNPM_UPDATE_NOTIFIER: "false",
|
|
378
|
-
YARN_ENABLE_TELEMETRY: "0",
|
|
379
|
-
YARN_ENABLE_PROGRESS_BARS: "0",
|
|
380
|
-
// Cross-language/tooling non-interactive defaults.
|
|
381
|
-
CARGO_TERM_PROGRESS_WHEN: "never",
|
|
382
|
-
DEBIAN_FRONTEND: "noninteractive",
|
|
383
|
-
PIP_NO_INPUT: "1",
|
|
384
|
-
PIP_DISABLE_PIP_VERSION_CHECK: "1",
|
|
385
|
-
TF_INPUT: "0",
|
|
386
|
-
TF_IN_AUTOMATION: "1",
|
|
387
|
-
GH_PROMPT_DISABLED: "1",
|
|
388
|
-
COMPOSER_NO_INTERACTION: "1",
|
|
389
|
-
CLOUDSDK_CORE_DISABLE_PROMPTS: "1",
|
|
393
|
+
...NO_PAGER_ENV,
|
|
390
394
|
},
|
|
391
395
|
signal: options.signal,
|
|
392
396
|
cols,
|
|
393
397
|
rows,
|
|
394
398
|
},
|
|
395
399
|
(err, chunk) => {
|
|
396
|
-
if (err || !chunk) return;
|
|
400
|
+
if (finished || err || !chunk) return;
|
|
397
401
|
component.appendOutput(chunk);
|
|
398
402
|
const normalizedChunk = normalizeCaptureChunk(chunk);
|
|
399
403
|
pendingChunks = pendingChunks.then(() => sink.push(normalizedChunk)).catch(() => {});
|
package/src/tools/bash.ts
CHANGED
|
@@ -17,7 +17,7 @@ import { CachedOutputBlock } from "../tui/output-block";
|
|
|
17
17
|
import type { ToolSession } from ".";
|
|
18
18
|
import { type BashInteractiveResult, runInteractiveBashPty } from "./bash-interactive";
|
|
19
19
|
import { checkBashInterception } from "./bash-interceptor";
|
|
20
|
-
import { applyHeadTail
|
|
20
|
+
import { applyHeadTail } from "./bash-normalize";
|
|
21
21
|
import { expandInternalUrls } from "./bash-skill-urls";
|
|
22
22
|
import type { OutputMeta } from "./output-meta";
|
|
23
23
|
import { allocateOutputArtifact, createTailBuffer } from "./output-utils";
|
|
@@ -75,13 +75,11 @@ export class BashTool implements AgentTool<typeof bashSchema, BashToolDetails> {
|
|
|
75
75
|
onUpdate?: AgentToolUpdateCallback<BashToolDetails>,
|
|
76
76
|
ctx?: AgentToolContext,
|
|
77
77
|
): Promise<AgentToolResult<BashToolDetails>> {
|
|
78
|
-
|
|
79
|
-
const normalized = normalizeBashCommand(rawCommand);
|
|
80
|
-
let command = normalized.command;
|
|
78
|
+
let command = rawCommand;
|
|
81
79
|
|
|
82
|
-
//
|
|
83
|
-
const headLines = head
|
|
84
|
-
const tailLines = tail
|
|
80
|
+
// Only apply explicit head/tail params from tool input.
|
|
81
|
+
const headLines = head;
|
|
82
|
+
const tailLines = tail;
|
|
85
83
|
|
|
86
84
|
// Check interception if enabled and available tools are known
|
|
87
85
|
if (this.session.settings.get("bashInterceptor.enabled")) {
|
|
@@ -140,6 +138,7 @@ export class BashTool implements AgentTool<typeof bashSchema, BashToolDetails> {
|
|
|
140
138
|
})
|
|
141
139
|
: await executeBash(command, {
|
|
142
140
|
cwd: commandCwd,
|
|
141
|
+
sessionKey: this.session.getSessionId?.() ?? undefined,
|
|
143
142
|
timeout: timeoutMs,
|
|
144
143
|
signal,
|
|
145
144
|
env: extraEnv,
|