@oh-my-pi/pi-coding-agent 14.9.1 → 14.9.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 +60 -0
- package/package.json +7 -7
- package/scripts/format-prompts.ts +3 -3
- package/src/config/prompt-templates.ts +0 -5
- package/src/config/settings-schema.ts +38 -0
- package/src/eval/eval.lark +10 -31
- package/src/eval/index.ts +1 -0
- package/src/eval/parse.ts +156 -255
- package/src/eval/sniff.ts +28 -0
- package/src/export/html/template.css +38 -0
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +209 -15
- package/src/extensibility/extensions/runner.ts +173 -177
- package/src/hashline/apply.ts +8 -24
- package/src/hashline/constants.ts +20 -0
- package/src/hashline/execute.ts +0 -1
- package/src/hashline/grammar.lark +16 -27
- package/src/hashline/hash.ts +4 -34
- package/src/hashline/input.ts +16 -2
- package/src/hashline/parser.ts +12 -40
- package/src/hashline/types.ts +1 -2
- package/src/internal-urls/agent-protocol.ts +1 -0
- package/src/internal-urls/artifact-protocol.ts +1 -0
- package/src/internal-urls/docs-index.generated.ts +2 -1
- package/src/internal-urls/jobs-protocol.ts +1 -0
- package/src/internal-urls/local-protocol.ts +1 -0
- package/src/internal-urls/mcp-protocol.ts +1 -0
- package/src/internal-urls/memory-protocol.ts +1 -0
- package/src/internal-urls/pi-protocol.ts +1 -0
- package/src/internal-urls/router.ts +2 -1
- package/src/internal-urls/rule-protocol.ts +1 -0
- package/src/internal-urls/skill-protocol.ts +1 -0
- package/src/internal-urls/types.ts +18 -2
- package/src/mcp/transports/http.ts +49 -47
- package/src/prompts/system/custom-system-prompt.md +0 -2
- package/src/prompts/system/now-prompt.md +7 -0
- package/src/prompts/system/project-prompt.md +2 -0
- package/src/prompts/system/subagent-system-prompt.md +18 -9
- package/src/prompts/system/subagent-user-prompt.md +1 -10
- package/src/prompts/system/system-prompt.md +154 -233
- package/src/prompts/tools/bash.md +0 -24
- package/src/prompts/tools/eval.md +26 -13
- package/src/prompts/tools/hashline.md +1 -4
- package/src/sdk.ts +12 -22
- package/src/session/agent-session.ts +49 -17
- package/src/system-prompt.ts +38 -104
- package/src/task/executor.ts +15 -9
- package/src/task/index.ts +38 -33
- package/src/task/render.ts +4 -2
- package/src/tools/bash.ts +15 -41
- package/src/tools/eval.ts +13 -36
- package/src/tools/index.ts +0 -3
- package/src/tools/path-utils.ts +21 -1
- package/src/tools/read.ts +71 -49
- package/src/tools/search.ts +13 -1
- package/src/utils/file-display-mode.ts +11 -5
- package/src/workspace-tree.ts +210 -410
- package/src/task/template.ts +0 -47
- package/src/tools/bash-normalize.ts +0 -107
package/src/sdk.ts
CHANGED
|
@@ -100,9 +100,7 @@ import { SessionManager } from "./session/session-manager";
|
|
|
100
100
|
import { closeAllConnections } from "./ssh/connection-manager";
|
|
101
101
|
import { unmountAll } from "./ssh/sshfs-mount";
|
|
102
102
|
import {
|
|
103
|
-
type AgentsMdSearch,
|
|
104
103
|
type BuildSystemPromptResult,
|
|
105
|
-
buildAgentsMdSearch,
|
|
106
104
|
buildSystemPrompt as buildSystemPromptInternal,
|
|
107
105
|
buildSystemPromptToolMetadata,
|
|
108
106
|
loadProjectContextFiles as loadContextFilesInternal,
|
|
@@ -201,8 +199,6 @@ export interface CreateAgentSessionOptions {
|
|
|
201
199
|
rules?: Rule[];
|
|
202
200
|
/** Context files (AGENTS.md content). Default: discovered walking up from cwd */
|
|
203
201
|
contextFiles?: Array<{ path: string; content: string }>;
|
|
204
|
-
/** Pre-built AGENTS.md search (skips re-scanning the workspace; passed by parents to subagents). */
|
|
205
|
-
agentsMdSearch?: AgentsMdSearch;
|
|
206
202
|
/** Pre-built workspace tree (skips re-scanning; passed by parents to subagents). */
|
|
207
203
|
workspaceTree?: WorkspaceTree;
|
|
208
204
|
/** Prompt templates. Default: discovered from cwd/.omp/prompts/ + agentDir/prompts/ */
|
|
@@ -691,16 +687,14 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
691
687
|
if (!options.modelRegistry) {
|
|
692
688
|
modelRegistry.refreshInBackground();
|
|
693
689
|
}
|
|
694
|
-
// Kick off
|
|
695
|
-
//
|
|
696
|
-
//
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
: logger.time("buildAgentsMdSearch", buildAgentsMdSearch, cwd);
|
|
700
|
-
agentsMdSearchPromise.catch(() => {});
|
|
690
|
+
// Kick off workspace tree discovery early. The native workspace scan returns
|
|
691
|
+
// both the rendered-tree input and the AGENTS.md directory-context index, so
|
|
692
|
+
// startup does not perform a second recursive filesystem search. Subagents
|
|
693
|
+
// inherit the parent's resolved values via options.
|
|
694
|
+
const STARTUP_SCAN_DEADLINE_MS = 5000;
|
|
701
695
|
const workspaceTreePromise: Promise<WorkspaceTree> = options.workspaceTree
|
|
702
696
|
? Promise.resolve(options.workspaceTree)
|
|
703
|
-
: logger.time("buildWorkspaceTree", buildWorkspaceTree,
|
|
697
|
+
: logger.time("buildWorkspaceTree", () => buildWorkspaceTree(cwd, { timeoutMs: STARTUP_SCAN_DEADLINE_MS }));
|
|
704
698
|
workspaceTreePromise.catch(() => {});
|
|
705
699
|
|
|
706
700
|
// Independent discoveries that depend only on cwd/agentDir — kicked off in parallel and awaited
|
|
@@ -898,12 +892,11 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
898
892
|
return { ttsrManager, rulebookRules, alwaysApplyRules };
|
|
899
893
|
});
|
|
900
894
|
|
|
901
|
-
// Resolve contextFiles up-front (it's needed before tool creation). The
|
|
902
|
-
//
|
|
903
|
-
//
|
|
904
|
-
// re-race
|
|
905
|
-
//
|
|
906
|
-
const STARTUP_SCAN_DEADLINE_MS = 5000;
|
|
895
|
+
// Resolve contextFiles up-front (it's needed before tool creation). The
|
|
896
|
+
// workspace tree scan is slow on large repos and we MUST NOT block startup on
|
|
897
|
+
// it. On timeout we forward `undefined` to ToolSession; buildSystemPromptInternal
|
|
898
|
+
// will re-race the same promise through its own withDeadline path. Background
|
|
899
|
+
// work continues so caches still warm.
|
|
907
900
|
const raceWithDeadline = <T>(name: string, work: Promise<T>): Promise<T | undefined> =>
|
|
908
901
|
Promise.race([
|
|
909
902
|
work,
|
|
@@ -916,9 +909,8 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
916
909
|
return undefined;
|
|
917
910
|
}),
|
|
918
911
|
]);
|
|
919
|
-
const [contextFiles,
|
|
912
|
+
const [contextFiles, resolvedWorkspaceTree] = await Promise.all([
|
|
920
913
|
contextFilesPromise,
|
|
921
|
-
raceWithDeadline("buildAgentsMdSearch", agentsMdSearchPromise),
|
|
922
914
|
raceWithDeadline("buildWorkspaceTree", workspaceTreePromise),
|
|
923
915
|
]);
|
|
924
916
|
|
|
@@ -1004,7 +996,6 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1004
996
|
},
|
|
1005
997
|
skipPythonPreflight: options.skipPythonPreflight,
|
|
1006
998
|
contextFiles,
|
|
1007
|
-
agentsMdSearch: resolvedAgentsMdSearch,
|
|
1008
999
|
workspaceTree: resolvedWorkspaceTree,
|
|
1009
1000
|
skills,
|
|
1010
1001
|
eventBus,
|
|
@@ -1456,7 +1447,6 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1456
1447
|
mcpDiscoveryServerSummaries: discoverableToolSummary.servers.map(formatDiscoverableMCPToolServerSummary),
|
|
1457
1448
|
eagerTasks,
|
|
1458
1449
|
secretsEnabled,
|
|
1459
|
-
agentsMdSearch: agentsMdSearchPromise,
|
|
1460
1450
|
workspaceTree: workspaceTreePromise,
|
|
1461
1451
|
});
|
|
1462
1452
|
|
|
@@ -570,6 +570,7 @@ export class AgentSession {
|
|
|
570
570
|
#agentId: string | undefined;
|
|
571
571
|
#agentRegistry: AgentRegistry | undefined;
|
|
572
572
|
#providerSessionId: string | undefined;
|
|
573
|
+
#isDisposed = false;
|
|
573
574
|
// Extension system
|
|
574
575
|
#extensionRunner: ExtensionRunner | undefined = undefined;
|
|
575
576
|
#turnIndex = 0;
|
|
@@ -646,23 +647,32 @@ export class AgentSession {
|
|
|
646
647
|
#hindsightSessionState: HindsightSessionState | undefined = undefined;
|
|
647
648
|
readonly rawSseDebugBuffer: RawSseDebugBuffer;
|
|
648
649
|
|
|
649
|
-
#
|
|
650
|
-
if (process.platform !== "darwin")
|
|
651
|
-
|
|
652
|
-
|
|
650
|
+
#acquirePowerAssertion(): void {
|
|
651
|
+
if (process.platform !== "darwin") return;
|
|
652
|
+
if (this.#powerAssertion) return;
|
|
653
|
+
const idle = this.settings.get("power.preventIdleSleep");
|
|
654
|
+
const system = this.settings.get("power.preventSystemSleep");
|
|
655
|
+
const user = this.settings.get("power.declareUserActive");
|
|
656
|
+
const display = this.settings.get("power.preventDisplaySleep");
|
|
657
|
+
// All four off → user opted out; do nothing.
|
|
658
|
+
if (!idle && !system && !user && !display) return;
|
|
653
659
|
try {
|
|
654
|
-
this.#powerAssertion = MacOSPowerAssertion.start({
|
|
660
|
+
this.#powerAssertion = MacOSPowerAssertion.start({
|
|
661
|
+
reason: "Oh My Pi agent session",
|
|
662
|
+
idle,
|
|
663
|
+
system,
|
|
664
|
+
user,
|
|
665
|
+
display,
|
|
666
|
+
});
|
|
655
667
|
} catch (error) {
|
|
656
668
|
logger.warn("Failed to acquire macOS power assertion", { error: String(error) });
|
|
657
669
|
}
|
|
658
670
|
}
|
|
659
671
|
|
|
660
|
-
#
|
|
672
|
+
#releasePowerAssertion(): void {
|
|
661
673
|
const assertion = this.#powerAssertion;
|
|
662
674
|
this.#powerAssertion = undefined;
|
|
663
|
-
if (!assertion)
|
|
664
|
-
return;
|
|
665
|
-
}
|
|
675
|
+
if (!assertion) return;
|
|
666
676
|
try {
|
|
667
677
|
assertion.stop();
|
|
668
678
|
} catch (error) {
|
|
@@ -670,11 +680,30 @@ export class AgentSession {
|
|
|
670
680
|
}
|
|
671
681
|
}
|
|
672
682
|
|
|
683
|
+
#beginInFlight(): void {
|
|
684
|
+
this.#promptInFlightCount++;
|
|
685
|
+
if (this.#promptInFlightCount === 1) {
|
|
686
|
+
this.#acquirePowerAssertion();
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
#endInFlight(): void {
|
|
691
|
+
this.#promptInFlightCount = Math.max(0, this.#promptInFlightCount - 1);
|
|
692
|
+
if (this.#promptInFlightCount === 0) {
|
|
693
|
+
this.#releasePowerAssertion();
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
#resetInFlight(): void {
|
|
698
|
+
this.#promptInFlightCount = 0;
|
|
699
|
+
this.#releasePowerAssertion();
|
|
700
|
+
}
|
|
701
|
+
|
|
673
702
|
constructor(config: AgentSessionConfig) {
|
|
674
703
|
this.agent = config.agent;
|
|
675
704
|
this.sessionManager = config.sessionManager;
|
|
676
705
|
this.settings = config.settings;
|
|
677
|
-
|
|
706
|
+
// Power assertions are taken per turn (see #beginInFlight); nothing acquired here.
|
|
678
707
|
this.#asyncJobManager = config.asyncJobManager;
|
|
679
708
|
this.#evalKernelOwnerId = config.evalKernelOwnerId ?? `agent-session:${Snowflake.next()}`;
|
|
680
709
|
this.#scopedModels = config.scopedModels ?? [];
|
|
@@ -2108,6 +2137,8 @@ export class AgentSession {
|
|
|
2108
2137
|
* Call this when completely done with the session.
|
|
2109
2138
|
*/
|
|
2110
2139
|
async dispose(): Promise<void> {
|
|
2140
|
+
this.#isDisposed = true;
|
|
2141
|
+
this.#pendingBackgroundExchanges = [];
|
|
2111
2142
|
this.#evalExecutionDisposing = true;
|
|
2112
2143
|
try {
|
|
2113
2144
|
if (this.#extensionRunner?.hasHandlers("session_shutdown")) {
|
|
@@ -2130,7 +2161,7 @@ export class AgentSession {
|
|
|
2130
2161
|
);
|
|
2131
2162
|
}
|
|
2132
2163
|
await disposeKernelSessionsByOwner(this.#evalKernelOwnerId);
|
|
2133
|
-
this.#
|
|
2164
|
+
this.#releasePowerAssertion();
|
|
2134
2165
|
await this.sessionManager.close();
|
|
2135
2166
|
this.#closeAllProviderSessions("dispose");
|
|
2136
2167
|
const hindsightState = this.setHindsightSessionState(undefined);
|
|
@@ -3171,7 +3202,7 @@ export class AgentSession {
|
|
|
3171
3202
|
skipPostPromptRecoveryWait?: boolean;
|
|
3172
3203
|
},
|
|
3173
3204
|
): Promise<void> {
|
|
3174
|
-
this.#
|
|
3205
|
+
this.#beginInFlight();
|
|
3175
3206
|
const generation = this.#promptGeneration;
|
|
3176
3207
|
try {
|
|
3177
3208
|
// Flush any pending bash messages before the new prompt
|
|
@@ -3291,7 +3322,7 @@ export class AgentSession {
|
|
|
3291
3322
|
await this.#waitForPostPromptRecovery();
|
|
3292
3323
|
}
|
|
3293
3324
|
} finally {
|
|
3294
|
-
this.#
|
|
3325
|
+
this.#endInFlight();
|
|
3295
3326
|
}
|
|
3296
3327
|
}
|
|
3297
3328
|
|
|
@@ -3877,7 +3908,7 @@ export class AgentSession {
|
|
|
3877
3908
|
// Clear prompt-in-flight state: waitForIdle resolves when the agent loop's finally
|
|
3878
3909
|
// block runs, but nested prompt setup/finalizers may still be unwinding. Without this,
|
|
3879
3910
|
// a subsequent prompt() can incorrectly observe the session as busy after an abort.
|
|
3880
|
-
this.#
|
|
3911
|
+
this.#resetInFlight();
|
|
3881
3912
|
// Safety net: if the agent loop aborted without producing an assistant
|
|
3882
3913
|
// message (e.g. failed before the first stream), the in-flight yield was
|
|
3883
3914
|
// never resolved or rejected by the normal message_end path. Reject it now
|
|
@@ -4699,7 +4730,7 @@ export class AgentSession {
|
|
|
4699
4730
|
if (handoffSignal.aborted) {
|
|
4700
4731
|
throw new Error("Handoff cancelled");
|
|
4701
4732
|
}
|
|
4702
|
-
this.#
|
|
4733
|
+
this.#beginInFlight();
|
|
4703
4734
|
try {
|
|
4704
4735
|
this.agent.setSystemPrompt(this.#baseSystemPrompt);
|
|
4705
4736
|
await this.#promptAgentWithIdleRetry([
|
|
@@ -4711,7 +4742,7 @@ export class AgentSession {
|
|
|
4711
4742
|
},
|
|
4712
4743
|
]);
|
|
4713
4744
|
} finally {
|
|
4714
|
-
this.#
|
|
4745
|
+
this.#endInFlight();
|
|
4715
4746
|
}
|
|
4716
4747
|
await completionPromise;
|
|
4717
4748
|
|
|
@@ -6853,7 +6884,8 @@ export class AgentSession {
|
|
|
6853
6884
|
if (this.#scheduledBackgroundExchangeFlush) return;
|
|
6854
6885
|
this.#scheduledBackgroundExchangeFlush = true;
|
|
6855
6886
|
const attempt = (): void => {
|
|
6856
|
-
if (this.#pendingBackgroundExchanges.length === 0) {
|
|
6887
|
+
if (this.#pendingBackgroundExchanges.length === 0 || this.#isDisposed) {
|
|
6888
|
+
this.#pendingBackgroundExchanges = [];
|
|
6857
6889
|
this.#scheduledBackgroundExchangeFlush = false;
|
|
6858
6890
|
return;
|
|
6859
6891
|
}
|
package/src/system-prompt.ts
CHANGED
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
import * as os from "node:os";
|
|
6
6
|
import type { AgentTool } from "@oh-my-pi/pi-agent-core";
|
|
7
|
-
import { FileType, glob } from "@oh-my-pi/pi-natives";
|
|
8
7
|
import { $env, getGpuCachePath, getProjectDir, hasFsCode, isEnoent, logger, prompt } from "@oh-my-pi/pi-utils";
|
|
9
8
|
import { $ } from "bun";
|
|
10
9
|
import { contextFileCapability } from "./capability/context-file";
|
|
@@ -13,9 +12,11 @@ import type { SkillsSettings } from "./config/settings";
|
|
|
13
12
|
import { type ContextFile, loadCapability, type SystemPrompt as SystemPromptFile } from "./discovery";
|
|
14
13
|
import { loadSkills, type Skill } from "./extensibility/skills";
|
|
15
14
|
import customSystemPromptTemplate from "./prompts/system/custom-system-prompt.md" with { type: "text" };
|
|
15
|
+
import nowPromptTemplate from "./prompts/system/now-prompt.md" with { type: "text" };
|
|
16
16
|
import projectPromptTemplate from "./prompts/system/project-prompt.md" with { type: "text" };
|
|
17
17
|
import systemPromptTemplate from "./prompts/system/system-prompt.md" with { type: "text" };
|
|
18
|
-
import {
|
|
18
|
+
import { shortenPath } from "./tools/render-utils";
|
|
19
|
+
import { AGENTS_MD_LIMIT, buildWorkspaceTree, type WorkspaceTree } from "./workspace-tree";
|
|
19
20
|
|
|
20
21
|
interface AlwaysApplyRule {
|
|
21
22
|
name: string;
|
|
@@ -84,58 +85,7 @@ function parseWmicTable(output: string, header: string): string | null {
|
|
|
84
85
|
return filtered[0] ?? null;
|
|
85
86
|
}
|
|
86
87
|
|
|
87
|
-
const AGENTS_MD_MIN_DEPTH = 1;
|
|
88
|
-
const AGENTS_MD_MAX_DEPTH = 4;
|
|
89
|
-
const AGENTS_MD_LIMIT = 200;
|
|
90
88
|
const SYSTEM_PROMPT_PREP_TIMEOUT_MS = 5000;
|
|
91
|
-
const AGENTS_MD_EXCLUDED_DIRS = new Set(["node_modules", ".git"]);
|
|
92
|
-
|
|
93
|
-
export interface AgentsMdSearch {
|
|
94
|
-
scopePath: string;
|
|
95
|
-
limit: number;
|
|
96
|
-
pattern: string;
|
|
97
|
-
files: string[];
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
async function listAgentsMdFiles(root: string, limit: number): Promise<string[]> {
|
|
101
|
-
try {
|
|
102
|
-
const result = await glob({
|
|
103
|
-
pattern: "**/AGENTS.md",
|
|
104
|
-
path: root,
|
|
105
|
-
fileType: FileType.File,
|
|
106
|
-
recursive: true,
|
|
107
|
-
hidden: false,
|
|
108
|
-
gitignore: true,
|
|
109
|
-
maxResults: limit * 4,
|
|
110
|
-
cache: true,
|
|
111
|
-
});
|
|
112
|
-
const files: string[] = [];
|
|
113
|
-
for (const m of result.matches) {
|
|
114
|
-
const rel = m.path.replace(/\\/g, "/");
|
|
115
|
-
if (!rel?.endsWith("AGENTS.md")) continue;
|
|
116
|
-
const segments = rel.split("/");
|
|
117
|
-
const depth = segments.length - 1;
|
|
118
|
-
if (depth < AGENTS_MD_MIN_DEPTH || depth > AGENTS_MD_MAX_DEPTH) continue;
|
|
119
|
-
const dirSegments = segments.slice(0, -1);
|
|
120
|
-
if (dirSegments.some(seg => AGENTS_MD_EXCLUDED_DIRS.has(seg) || seg.startsWith("."))) continue;
|
|
121
|
-
files.push(rel);
|
|
122
|
-
if (files.length >= limit) break;
|
|
123
|
-
}
|
|
124
|
-
return Array.from(new Set(files)).sort().slice(0, limit);
|
|
125
|
-
} catch {
|
|
126
|
-
return [];
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
export async function buildAgentsMdSearch(cwd: string): Promise<AgentsMdSearch> {
|
|
131
|
-
const files = await listAgentsMdFiles(cwd, AGENTS_MD_LIMIT);
|
|
132
|
-
return {
|
|
133
|
-
scopePath: ".",
|
|
134
|
-
limit: AGENTS_MD_LIMIT,
|
|
135
|
-
pattern: `AGENTS.md depth ${AGENTS_MD_MIN_DEPTH}-${AGENTS_MD_MAX_DEPTH}`,
|
|
136
|
-
files,
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
89
|
|
|
140
90
|
async function getGpuModel(): Promise<string | null> {
|
|
141
91
|
switch (process.platform) {
|
|
@@ -409,8 +359,6 @@ export interface BuildSystemPromptOptions {
|
|
|
409
359
|
alwaysApplyRules?: AlwaysApplyRule[];
|
|
410
360
|
/** Whether secret obfuscation is active. When true, explains the redaction format in the prompt. */
|
|
411
361
|
secretsEnabled?: boolean;
|
|
412
|
-
/** Pre-loaded AGENTS.md search (skips discovery if provided). May be a Promise to allow early kick-off. */
|
|
413
|
-
agentsMdSearch?: AgentsMdSearch | Promise<AgentsMdSearch>;
|
|
414
362
|
/** Pre-loaded workspace tree (skips discovery if provided). May be a Promise to allow early kick-off. */
|
|
415
363
|
workspaceTree?: WorkspaceTree | Promise<WorkspaceTree>;
|
|
416
364
|
}
|
|
@@ -444,7 +392,6 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
444
392
|
mcpDiscoveryServerSummaries = [],
|
|
445
393
|
eagerTasks = false,
|
|
446
394
|
secretsEnabled = false,
|
|
447
|
-
agentsMdSearch: providedAgentsMdSearch,
|
|
448
395
|
workspaceTree: providedWorkspaceTree,
|
|
449
396
|
} = options;
|
|
450
397
|
const resolvedCwd = cwd ?? getProjectDir();
|
|
@@ -454,18 +401,13 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
454
401
|
resolvedAppendPrompt: undefined as string | undefined,
|
|
455
402
|
systemPromptCustomization: null as string | null,
|
|
456
403
|
contextFiles: dedupeExactContextFiles(providedContextFiles ?? []),
|
|
457
|
-
agentsMdSearch: {
|
|
458
|
-
scopePath: ".",
|
|
459
|
-
limit: AGENTS_MD_LIMIT,
|
|
460
|
-
pattern: `AGENTS.md depth ${AGENTS_MD_MIN_DEPTH}-${AGENTS_MD_MAX_DEPTH}`,
|
|
461
|
-
files: [] as string[],
|
|
462
|
-
} satisfies AgentsMdSearch,
|
|
463
404
|
skills: providedSkills ?? ([] as Skill[]),
|
|
464
405
|
workspaceTree: {
|
|
465
406
|
rootPath: resolvedCwd,
|
|
466
407
|
rendered: "",
|
|
467
408
|
truncated: false,
|
|
468
409
|
totalLines: 0,
|
|
410
|
+
agentsMdFiles: [],
|
|
469
411
|
} satisfies WorkspaceTree,
|
|
470
412
|
};
|
|
471
413
|
|
|
@@ -503,14 +445,12 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
503
445
|
const contextFilesPromise = providedContextFiles
|
|
504
446
|
? Promise.resolve(providedContextFiles)
|
|
505
447
|
: logger.time("loadProjectContextFiles", loadProjectContextFiles, { cwd: resolvedCwd });
|
|
506
|
-
const agentsMdSearchPromise =
|
|
507
|
-
providedAgentsMdSearch !== undefined
|
|
508
|
-
? Promise.resolve(providedAgentsMdSearch)
|
|
509
|
-
: logger.time("buildAgentsMdSearch", buildAgentsMdSearch, resolvedCwd);
|
|
510
448
|
const workspaceTreePromise =
|
|
511
449
|
providedWorkspaceTree !== undefined
|
|
512
450
|
? Promise.resolve(providedWorkspaceTree)
|
|
513
|
-
: logger.time("buildWorkspaceTree",
|
|
451
|
+
: logger.time("buildWorkspaceTree", () =>
|
|
452
|
+
buildWorkspaceTree(resolvedCwd, { timeoutMs: SYSTEM_PROMPT_PREP_TIMEOUT_MS }),
|
|
453
|
+
);
|
|
514
454
|
const skillsPromise: Promise<Skill[]> =
|
|
515
455
|
providedSkills !== undefined
|
|
516
456
|
? Promise.resolve(providedSkills)
|
|
@@ -518,33 +458,30 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
518
458
|
? loadSkills({ ...skillsSettings, cwd: resolvedCwd }).then(result => result.skills)
|
|
519
459
|
: Promise.resolve([]);
|
|
520
460
|
|
|
521
|
-
const [
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
prepDefaults.
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
)
|
|
544
|
-
|
|
545
|
-
withDeadline("loadSkills", skillsPromise, prepDefaults.skills),
|
|
546
|
-
withDeadline("buildWorkspaceTree", workspaceTreePromise, prepDefaults.workspaceTree),
|
|
547
|
-
]);
|
|
461
|
+
const [resolvedCustomPrompt, resolvedAppendPrompt, systemPromptCustomization, contextFiles, skills, workspaceTree] =
|
|
462
|
+
await Promise.all([
|
|
463
|
+
withDeadline(
|
|
464
|
+
"customPrompt",
|
|
465
|
+
resolvePromptInput(customPrompt, "system prompt"),
|
|
466
|
+
prepDefaults.resolvedCustomPrompt,
|
|
467
|
+
),
|
|
468
|
+
withDeadline(
|
|
469
|
+
"appendSystemPrompt",
|
|
470
|
+
resolvePromptInput(appendSystemPrompt, "append system prompt"),
|
|
471
|
+
prepDefaults.resolvedAppendPrompt,
|
|
472
|
+
),
|
|
473
|
+
withDeadline(
|
|
474
|
+
"loadSystemPromptFiles",
|
|
475
|
+
systemPromptCustomizationPromise,
|
|
476
|
+
prepDefaults.systemPromptCustomization,
|
|
477
|
+
),
|
|
478
|
+
withDeadline("loadProjectContextFiles", contextFilesPromise, prepDefaults.contextFiles).then(
|
|
479
|
+
dedupeExactContextFiles,
|
|
480
|
+
),
|
|
481
|
+
withDeadline("loadSkills", skillsPromise, prepDefaults.skills),
|
|
482
|
+
withDeadline("buildWorkspaceTree", workspaceTreePromise, prepDefaults.workspaceTree),
|
|
483
|
+
]);
|
|
484
|
+
const agentsMdFiles = Array.from(new Set(workspaceTree.agentsMdFiles)).sort().slice(0, AGENTS_MD_LIMIT);
|
|
548
485
|
|
|
549
486
|
if (timedOut.length > 0) {
|
|
550
487
|
logger.warn("System prompt preparation steps timed out; using minimal fallback for those steps", {
|
|
@@ -568,7 +505,7 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
568
505
|
|
|
569
506
|
const date = new Date().toISOString().slice(0, 10);
|
|
570
507
|
const dateTime = date;
|
|
571
|
-
const promptCwd = resolvedCwd.replace(/\\/g, "/");
|
|
508
|
+
const promptCwd = shortenPath(resolvedCwd.replace(/\\/g, "/"));
|
|
572
509
|
|
|
573
510
|
// Build tool metadata for system prompt rendering
|
|
574
511
|
// Priority: explicit list > tools map > defaults
|
|
@@ -606,7 +543,6 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
606
543
|
const injectedAlwaysApplyRules = dedupeAlwaysApplyRules(alwaysApplyRules, promptSources);
|
|
607
544
|
|
|
608
545
|
const environment = await logger.time("getEnvironmentInfo", getEnvironmentInfo);
|
|
609
|
-
const reportToolIssueToolName = toolPromptNames.get("report_tool_issue") ?? "report_tool_issue";
|
|
610
546
|
const data = {
|
|
611
547
|
systemPromptCustomization: effectiveSystemPromptCustomization,
|
|
612
548
|
customPrompt: resolvedCustomPrompt,
|
|
@@ -617,7 +553,7 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
617
553
|
toolRefs,
|
|
618
554
|
environment,
|
|
619
555
|
contextFiles,
|
|
620
|
-
agentsMdSearch,
|
|
556
|
+
agentsMdSearch: { files: agentsMdFiles },
|
|
621
557
|
workspaceTree,
|
|
622
558
|
skills: filteredSkills,
|
|
623
559
|
rules: rules ?? [],
|
|
@@ -633,18 +569,16 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
633
569
|
eagerTasks,
|
|
634
570
|
secretsEnabled,
|
|
635
571
|
};
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
// When autoqa is active the report_tool_issue tool is in the tool set — nudge the agent.
|
|
639
|
-
if (toolNames.includes("report_tool_issue")) {
|
|
640
|
-
rendered += `\n\n<critical>\nThe \`${reportToolIssueToolName}\` tool is available for automated QA. If ANY tool you call returns output that is unexpected, incorrect, malformed, or otherwise inconsistent with what you anticipated given the tool's described behavior and your parameters, call \`${reportToolIssueToolName}\` with the tool name and a concise description of the discrepancy. Do not hesitate to report — false positives are acceptable.\n</critical>`;
|
|
641
|
-
}
|
|
642
|
-
|
|
572
|
+
const rendered = prompt.render(resolvedCustomPrompt ? customSystemPromptTemplate : systemPromptTemplate, data);
|
|
643
573
|
const systemPrompt = [rendered];
|
|
644
574
|
const projectPrompt = resolvedCustomPrompt ? "" : prompt.render(projectPromptTemplate, data).trim();
|
|
645
575
|
if (projectPrompt) {
|
|
646
576
|
systemPrompt.push(projectPrompt);
|
|
647
577
|
}
|
|
578
|
+
const nowPrompt = prompt.render(nowPromptTemplate, data).trim();
|
|
579
|
+
if (nowPrompt) {
|
|
580
|
+
systemPrompt.push(nowPrompt);
|
|
581
|
+
}
|
|
648
582
|
|
|
649
583
|
return { systemPrompt };
|
|
650
584
|
}
|
package/src/task/executor.ts
CHANGED
|
@@ -28,7 +28,6 @@ import { createAgentSession, discoverAuthStorage } from "../sdk";
|
|
|
28
28
|
import type { AgentSession, AgentSessionEvent } from "../session/agent-session";
|
|
29
29
|
import type { AuthStorage } from "../session/auth-storage";
|
|
30
30
|
import { SessionManager } from "../session/session-manager";
|
|
31
|
-
import type { AgentsMdSearch } from "../system-prompt";
|
|
32
31
|
import { type ContextFileEntry, truncateTail } from "../tools";
|
|
33
32
|
import { jtdToJsonSchema, normalizeSchema } from "../tools/jtd-to-json-schema";
|
|
34
33
|
import { ToolAbortError } from "../tools/tool-errors";
|
|
@@ -140,6 +139,7 @@ export interface ExecutorOptions {
|
|
|
140
139
|
agent: AgentDefinition;
|
|
141
140
|
task: string;
|
|
142
141
|
assignment?: string;
|
|
142
|
+
context?: string;
|
|
143
143
|
description?: string;
|
|
144
144
|
index: number;
|
|
145
145
|
id: string;
|
|
@@ -165,7 +165,6 @@ export interface ExecutorOptions {
|
|
|
165
165
|
contextFiles?: ContextFileEntry[];
|
|
166
166
|
skills?: Skill[];
|
|
167
167
|
promptTemplates?: PromptTemplate[];
|
|
168
|
-
agentsMdSearch?: AgentsMdSearch;
|
|
169
168
|
workspaceTree?: WorkspaceTree;
|
|
170
169
|
mcpManager?: MCPManager;
|
|
171
170
|
authStorage?: AuthStorage;
|
|
@@ -941,8 +940,13 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
941
940
|
checkAbort();
|
|
942
941
|
const authStorage = options.authStorage ?? (await discoverAuthStorage());
|
|
943
942
|
checkAbort();
|
|
943
|
+
const registryFromParent = options.modelRegistry !== undefined;
|
|
944
944
|
const modelRegistry = options.modelRegistry ?? new ModelRegistry(authStorage);
|
|
945
|
-
|
|
945
|
+
if (!registryFromParent) {
|
|
946
|
+
await modelRegistry.refresh();
|
|
947
|
+
} else {
|
|
948
|
+
logger.debug("runSubagent: reusing parent modelRegistry; skipping refresh");
|
|
949
|
+
}
|
|
946
950
|
checkAbort();
|
|
947
951
|
|
|
948
952
|
const {
|
|
@@ -990,19 +994,21 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
990
994
|
contextFiles: options.contextFiles,
|
|
991
995
|
skills: options.skills,
|
|
992
996
|
promptTemplates: options.promptTemplates,
|
|
993
|
-
agentsMdSearch: options.agentsMdSearch,
|
|
994
997
|
workspaceTree: options.workspaceTree,
|
|
995
|
-
systemPrompt: defaultPrompt =>
|
|
996
|
-
prompt.render(subagentSystemPromptTemplate, {
|
|
997
|
-
base: defaultPrompt.join("\n\n"),
|
|
998
|
+
systemPrompt: defaultPrompt => {
|
|
999
|
+
const subagentPrompt = prompt.render(subagentSystemPromptTemplate, {
|
|
998
1000
|
agent: agent.systemPrompt,
|
|
1001
|
+
context: options.context?.trim() ?? "",
|
|
999
1002
|
worktree: worktree ?? "",
|
|
1000
1003
|
outputSchema: normalizedOutputSchema,
|
|
1001
1004
|
contextFile: options.contextFile,
|
|
1002
1005
|
ircPeers: ircEnabled ? renderIrcPeerRoster(id) : "",
|
|
1003
1006
|
ircSelfId: ircEnabled ? id : "",
|
|
1004
|
-
})
|
|
1005
|
-
|
|
1007
|
+
});
|
|
1008
|
+
return defaultPrompt.length === 0
|
|
1009
|
+
? [subagentPrompt]
|
|
1010
|
+
: [...defaultPrompt.slice(0, -1), subagentPrompt, defaultPrompt[defaultPrompt.length - 1]];
|
|
1011
|
+
},
|
|
1006
1012
|
sessionManager,
|
|
1007
1013
|
hasUI: false,
|
|
1008
1014
|
spawns: spawnsEnv,
|