@oh-my-pi/pi-coding-agent 17.2.1 → 17.2.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 +47 -0
- package/dist/{CHANGELOG-dj46zzrm.md → CHANGELOG-xfpkakrn.md} +47 -0
- package/dist/cli.js +3891 -3885
- package/dist/types/config/keybindings.d.ts +6 -1
- package/dist/types/config/settings-schema.d.ts +16 -5
- package/dist/types/edit/hashline/block-resolver.d.ts +5 -5
- package/dist/types/edit/hashline/diff.d.ts +4 -4
- package/dist/types/eval/executor-base.d.ts +27 -0
- package/dist/types/eval/js/context-manager.d.ts +7 -0
- package/dist/types/eval/js/executor.d.ts +2 -0
- package/dist/types/extensibility/extensions/runner.d.ts +64 -5
- package/dist/types/extensibility/extensions/types.d.ts +15 -0
- package/dist/types/extensibility/extensions/wrapper.d.ts +1 -1
- package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +1 -1
- package/dist/types/live/visualizer.d.ts +3 -1
- package/dist/types/modes/acp/acp-agent.d.ts +3 -3
- package/dist/types/modes/components/keybinding-hints.d.ts +1 -1
- package/dist/types/modes/controllers/mcp-command-controller.d.ts +13 -0
- package/dist/types/modes/interactive-mode.d.ts +1 -0
- package/dist/types/modes/theme/theme.d.ts +1 -1
- package/dist/types/modes/types.d.ts +2 -0
- package/dist/types/secrets/index.d.ts +22 -0
- package/dist/types/secrets/obfuscator.d.ts +1 -1
- package/dist/types/session/agent-session-types.d.ts +4 -0
- package/dist/types/session/agent-session.d.ts +9 -0
- package/dist/types/session/codex-auto-reset.d.ts +182 -64
- package/dist/types/session/session-storage.d.ts +5 -2
- package/dist/types/session/session-tools.d.ts +2 -1
- package/dist/types/session/turn-recovery.d.ts +7 -1
- package/dist/types/system-prompt.d.ts +8 -0
- package/dist/types/task/executor.d.ts +27 -0
- package/dist/types/task/persisted-revive.d.ts +7 -0
- package/dist/types/tools/bash-interceptor.d.ts +1 -1
- package/dist/types/tools/browser/attach.d.ts +9 -1
- package/dist/types/tools/browser/launch.d.ts +20 -1
- package/dist/types/tools/browser/registry.d.ts +2 -0
- package/dist/types/tools/browser/tab-protocol.d.ts +8 -0
- package/dist/types/tools/browser/tab-supervisor.d.ts +1 -0
- package/dist/types/tools/browser/tab-worker.d.ts +3 -1
- package/dist/types/tools/shell-tokenize.d.ts +11 -0
- package/dist/types/utils/image-loading.d.ts +2 -0
- package/dist/types/web/search/providers/duckduckgo.d.ts +9 -0
- package/dist/types/web/search/types.d.ts +2 -2
- package/package.json +12 -12
- package/src/commit/agentic/index.ts +2 -1
- package/src/commit/pipeline.ts +3 -2
- package/src/config/keybindings.ts +6 -1
- package/src/config/settings-schema.ts +16 -3
- package/src/edit/hashline/block-resolver.ts +5 -5
- package/src/edit/hashline/diff.ts +22 -15
- package/src/edit/hashline/execute.ts +14 -13
- package/src/edit/renderer.ts +3 -3
- package/src/eval/executor-base.ts +39 -0
- package/src/eval/jl/executor.ts +13 -2
- package/src/eval/js/context-manager.ts +97 -14
- package/src/eval/js/executor.ts +3 -0
- package/src/eval/js/index.ts +1 -0
- package/src/eval/py/executor.ts +55 -15
- package/src/eval/rb/executor.ts +8 -1
- package/src/extensibility/extensions/runner.ts +121 -4
- package/src/extensibility/extensions/types.ts +15 -0
- package/src/extensibility/extensions/wrapper.ts +19 -2
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +5 -5
- package/src/live/visualizer.ts +8 -1
- package/src/lsp/index.ts +51 -11
- package/src/main.ts +3 -0
- package/src/modes/acp/acp-agent.ts +16 -5
- package/src/modes/components/assistant-message.ts +5 -10
- package/src/modes/components/custom-editor.ts +1 -1
- package/src/modes/components/keybinding-hints.ts +4 -13
- package/src/modes/components/status-line/segments.ts +4 -5
- package/src/modes/components/tool-execution.ts +4 -5
- package/src/modes/controllers/input-controller.ts +18 -40
- package/src/modes/controllers/live-command-controller.ts +1 -0
- package/src/modes/controllers/mcp-command-controller.ts +28 -11
- package/src/modes/interactive-mode.ts +59 -2
- package/src/modes/theme/theme.ts +13 -6
- package/src/modes/types.ts +2 -0
- package/src/modes/utils/hotkeys-markdown.ts +1 -0
- package/src/prompts/system/plan-mode-active.md +5 -5
- package/src/sdk.ts +47 -16
- package/src/secrets/index.ts +83 -3
- package/src/secrets/obfuscator.ts +70 -15
- package/src/session/agent-session-types.ts +7 -1
- package/src/session/agent-session.ts +393 -112
- package/src/session/codex-auto-reset.ts +594 -123
- package/src/session/eval-runner.ts +2 -0
- package/src/session/messages.ts +1 -1
- package/src/session/session-maintenance.ts +48 -1
- package/src/session/session-manager.ts +4 -3
- package/src/session/session-stats.ts +14 -3
- package/src/session/session-storage.ts +47 -15
- package/src/session/session-tools.ts +30 -2
- package/src/session/turn-recovery.ts +15 -2
- package/src/slash-commands/builtin-registry.ts +21 -15
- package/src/slash-commands/helpers/reset-usage.ts +2 -0
- package/src/system-prompt.ts +13 -1
- package/src/task/executor.ts +168 -15
- package/src/task/persisted-revive.ts +26 -1
- package/src/telemetry-export.ts +8 -4
- package/src/tools/bash-interceptor.ts +85 -8
- package/src/tools/bash.ts +1 -1
- package/src/tools/browser/attach.ts +35 -10
- package/src/tools/browser/launch.ts +60 -11
- package/src/tools/browser/registry.ts +18 -2
- package/src/tools/browser/tab-protocol.ts +8 -0
- package/src/tools/browser/tab-supervisor.ts +31 -4
- package/src/tools/browser/tab-worker.ts +32 -2
- package/src/tools/output-meta.ts +16 -1
- package/src/tools/shell-tokenize.ts +101 -0
- package/src/utils/image-loading.ts +8 -3
- package/src/utils/shell-snapshot.ts +31 -21
- package/src/web/search/providers/duckduckgo.ts +183 -15
- package/src/web/search/types.ts +6 -1
package/src/secrets/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as crypto from "node:crypto";
|
|
2
|
-
import * as fs from "node:fs
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
+
import { SENSITIVE_TOKEN_RE } from "@oh-my-pi/pi-ai/providers/transform-messages";
|
|
4
5
|
import { getAgentDir, isEnoent, logger } from "@oh-my-pi/pi-utils";
|
|
5
6
|
import { YAML } from "bun";
|
|
6
7
|
import { regexHasUnresolvableShortMatchFallback, type SecretEntry, sanitizeSecretFriendlyName } from "./obfuscator";
|
|
@@ -31,9 +32,9 @@ export async function getSecretPlaceholderKey(keyDir: string = getAgentDir()): P
|
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
const generated = crypto.randomBytes(32).toString("base64url");
|
|
34
|
-
await fs.mkdir(keyDir, { recursive: true });
|
|
35
|
+
await fs.promises.mkdir(keyDir, { recursive: true });
|
|
35
36
|
try {
|
|
36
|
-
await fs.writeFile(keyPath, generated, { flag: "wx", mode: 0o600 });
|
|
37
|
+
await fs.promises.writeFile(keyPath, generated, { flag: "wx", mode: 0o600 });
|
|
37
38
|
cachedPlaceholderKeys.set(keyPath, generated);
|
|
38
39
|
return generated;
|
|
39
40
|
} catch (err) {
|
|
@@ -71,6 +72,60 @@ export async function getExistingSecretPlaceholderKey(keyDir: string = getAgentD
|
|
|
71
72
|
return existing;
|
|
72
73
|
}
|
|
73
74
|
|
|
75
|
+
// Process-stable fallback for the sync lazy path when the key file cannot be
|
|
76
|
+
// persisted (e.g. unwritable config root on a headless run). Memoized so
|
|
77
|
+
// re-obfuscation within the process stays idempotent; placeholders simply lose
|
|
78
|
+
// cross-session stability, matching `defaultPlaceholderKey()` in obfuscator.ts.
|
|
79
|
+
let ephemeralSyncPlaceholderKey: string | undefined;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Synchronous variant of `getSecretPlaceholderKey` for the lazy key provider
|
|
83
|
+
* `SecretObfuscator` invokes inside its synchronous `obfuscate()` path when a
|
|
84
|
+
* built-in credential-pattern entry first matches session content. Never
|
|
85
|
+
* throws: an unreadable or unwritable key file degrades to a process-ephemeral
|
|
86
|
+
* key (with a warning) instead of breaking the session.
|
|
87
|
+
*/
|
|
88
|
+
export function getSecretPlaceholderKeySync(keyDir: string = getAgentDir()): string {
|
|
89
|
+
const keyPath = path.join(keyDir, "secret-placeholder.key");
|
|
90
|
+
const cached = cachedPlaceholderKeys.get(keyPath);
|
|
91
|
+
if (cached !== undefined) return cached;
|
|
92
|
+
try {
|
|
93
|
+
const existing = fs.readFileSync(keyPath, "utf8").trim();
|
|
94
|
+
if (PLACEHOLDER_KEY_RE.test(existing)) {
|
|
95
|
+
cachedPlaceholderKeys.set(keyPath, existing);
|
|
96
|
+
return existing;
|
|
97
|
+
}
|
|
98
|
+
} catch {
|
|
99
|
+
// Missing or unreadable — attempt creation below.
|
|
100
|
+
}
|
|
101
|
+
const generated = crypto.randomBytes(32).toString("base64url");
|
|
102
|
+
try {
|
|
103
|
+
fs.mkdirSync(keyDir, { recursive: true });
|
|
104
|
+
fs.writeFileSync(keyPath, generated, { flag: "wx", mode: 0o600 });
|
|
105
|
+
cachedPlaceholderKeys.set(keyPath, generated);
|
|
106
|
+
return generated;
|
|
107
|
+
} catch (err) {
|
|
108
|
+
if ((err as NodeJS.ErrnoException).code === "EEXIST") {
|
|
109
|
+
// Another process won the create race; accept its key if valid.
|
|
110
|
+
try {
|
|
111
|
+
const winner = fs.readFileSync(keyPath, "utf8").trim();
|
|
112
|
+
if (PLACEHOLDER_KEY_RE.test(winner)) {
|
|
113
|
+
cachedPlaceholderKeys.set(keyPath, winner);
|
|
114
|
+
return winner;
|
|
115
|
+
}
|
|
116
|
+
} catch {
|
|
117
|
+
// Fall through to the ephemeral key.
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
logger.warn("Could not persist secret placeholder key, using a process-ephemeral key", {
|
|
121
|
+
path: keyPath,
|
|
122
|
+
error: String(err),
|
|
123
|
+
});
|
|
124
|
+
ephemeralSyncPlaceholderKey ??= crypto.randomBytes(32).toString("base64url");
|
|
125
|
+
return ephemeralSyncPlaceholderKey;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
74
129
|
/** Read and validate the key file, optionally retrying briefly until a valid key lands. */
|
|
75
130
|
async function readPlaceholderKeyFile(keyPath: string, retry: boolean): Promise<string | undefined> {
|
|
76
131
|
const attempts = retry ? 50 : 1;
|
|
@@ -145,6 +200,31 @@ export function collectEnvSecrets(): SecretEntry[] {
|
|
|
145
200
|
return entries;
|
|
146
201
|
}
|
|
147
202
|
|
|
203
|
+
/**
|
|
204
|
+
* Built-in entries covering credential-shaped tokens (GitHub/GitLab/OpenAI-style
|
|
205
|
+
* API keys) that are NOT configured via secrets.yml or the environment. Without
|
|
206
|
+
* these, such a token in a tool result falls through to pi-ai's irreversible
|
|
207
|
+
* provider-boundary redaction (`[openai_token_redacted]`); the model then echoes
|
|
208
|
+
* that placeholder into edit-tool `old_text`, which can never match the real
|
|
209
|
+
* bytes on disk (issue #6968). Routing the same shapes through the obfuscator
|
|
210
|
+
* mints reversible keyed placeholders that `deobfuscateToolArguments` restores
|
|
211
|
+
* before tool execution, keeping exact-match edits working while the credential
|
|
212
|
+
* bytes still never reach the provider. Unlike the pi-ai redaction there is no
|
|
213
|
+
* entropy gate here — a false positive only over-obfuscates, which stays
|
|
214
|
+
* transparent because the round trip is lossless.
|
|
215
|
+
*/
|
|
216
|
+
export function builtinCredentialSecretEntries(): SecretEntry[] {
|
|
217
|
+
return [
|
|
218
|
+
{
|
|
219
|
+
type: "regex",
|
|
220
|
+
content: SENSITIVE_TOKEN_RE.source,
|
|
221
|
+
flags: "i",
|
|
222
|
+
mode: "obfuscate",
|
|
223
|
+
friendlyName: "Credential",
|
|
224
|
+
},
|
|
225
|
+
];
|
|
226
|
+
}
|
|
227
|
+
|
|
148
228
|
async function loadSecretsFile(filePath: string): Promise<SecretEntry[]> {
|
|
149
229
|
try {
|
|
150
230
|
const text = await Bun.file(filePath).text();
|
|
@@ -594,18 +594,22 @@ export class SecretObfuscator {
|
|
|
594
594
|
/** Whether any secrets were configured */
|
|
595
595
|
#hasAny: boolean;
|
|
596
596
|
|
|
597
|
-
/**
|
|
598
|
-
|
|
597
|
+
/**
|
|
598
|
+
* Private per-install (or per-process) key for the keyed placeholder digest.
|
|
599
|
+
* Resolved lazily when the constructor received a key PROVIDER: the first
|
|
600
|
+
* placeholder mint or keyed fallback marker triggers resolution, so callers
|
|
601
|
+
* can defer persisting `secret-placeholder.key` until a dynamic regex entry
|
|
602
|
+
* actually matches session content.
|
|
603
|
+
*/
|
|
604
|
+
#key: string | undefined;
|
|
605
|
+
#keyProvider: (() => string) | undefined;
|
|
599
606
|
|
|
600
|
-
constructor(entries: SecretEntry[], key: string = defaultPlaceholderKey()) {
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
// obfuscated (provider-visible) output as a one-way secret.
|
|
607
|
-
this.#replaceMappings.set(key, this.#generateSecretReplacement(key));
|
|
608
|
-
this.#configuredSecretValues.add(key);
|
|
607
|
+
constructor(entries: SecretEntry[], key: string | (() => string) = defaultPlaceholderKey()) {
|
|
608
|
+
if (typeof key === "function") {
|
|
609
|
+
this.#keyProvider = key;
|
|
610
|
+
} else {
|
|
611
|
+
this.#setPlaceholderKey(key);
|
|
612
|
+
}
|
|
609
613
|
// Collect every configured plain-secret literal AND compile every regex
|
|
610
614
|
// entry BEFORE minting any placeholder below, so a placeholder's friendly
|
|
611
615
|
// name (checked against both in `#createPlaceholder`) can never embed a
|
|
@@ -671,6 +675,44 @@ export class SecretObfuscator {
|
|
|
671
675
|
this.#hasAny = hasRealSec;
|
|
672
676
|
}
|
|
673
677
|
|
|
678
|
+
/**
|
|
679
|
+
* The keyed-hash key makes obfuscate-mode placeholder bases un-dictionaryable,
|
|
680
|
+
* but it can be persisted in a user-readable file (`secret-placeholder.key`).
|
|
681
|
+
* A prompt-injected tool read (read/bash) could otherwise surface it to the
|
|
682
|
+
* provider verbatim and undo that protection, so redact the key itself from
|
|
683
|
+
* obfuscated (provider-visible) output as a one-way secret.
|
|
684
|
+
*/
|
|
685
|
+
#setPlaceholderKey(key: string): void {
|
|
686
|
+
this.#key = key;
|
|
687
|
+
this.#replaceMappings.set(key, this.#generateSecretReplacement(key));
|
|
688
|
+
this.#configuredSecretValues.add(key);
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
/** Resolve the placeholder key once, minting its self-redaction on first use. */
|
|
692
|
+
#getKey(): string {
|
|
693
|
+
let key = this.#key;
|
|
694
|
+
if (key === undefined) {
|
|
695
|
+
key = this.#keyProvider?.() ?? defaultPlaceholderKey();
|
|
696
|
+
this.#keyProvider = undefined;
|
|
697
|
+
this.#setPlaceholderKey(key);
|
|
698
|
+
}
|
|
699
|
+
return key;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
/** Whether this pass will mint a keyed placeholder from a regex match. */
|
|
703
|
+
#willMintRegexPlaceholder(secretValues: ReadonlySet<string>): boolean {
|
|
704
|
+
for (const entry of this.#regexEntries) {
|
|
705
|
+
if (entry.mode !== "obfuscate") continue;
|
|
706
|
+
for (const value of secretValues) {
|
|
707
|
+
entry.regex.lastIndex = 0;
|
|
708
|
+
const matches = entry.regex.test(value);
|
|
709
|
+
entry.regex.lastIndex = 0;
|
|
710
|
+
if (matches) return true;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
return false;
|
|
714
|
+
}
|
|
715
|
+
|
|
674
716
|
hasSecrets(): boolean {
|
|
675
717
|
return this.#hasAny;
|
|
676
718
|
}
|
|
@@ -682,6 +724,13 @@ export class SecretObfuscator {
|
|
|
682
724
|
for (const secretValue of sharedRegexSecretValues ?? []) {
|
|
683
725
|
this.#currentRegexSecretValues.add(secretValue);
|
|
684
726
|
}
|
|
727
|
+
// Resolve a lazy key before the replace phase whenever this pass will mint
|
|
728
|
+
// a regex placeholder. The key registers itself as a replace-mode secret;
|
|
729
|
+
// resolving it later, while processing the regex match, would expose key
|
|
730
|
+
// bytes already present in this same provider-visible input.
|
|
731
|
+
if (this.#keyProvider !== undefined && this.#willMintRegexPlaceholder(this.#currentRegexSecretValues)) {
|
|
732
|
+
this.#getKey();
|
|
733
|
+
}
|
|
685
734
|
let result = text;
|
|
686
735
|
// `origin` runs parallel to `result` (one tag char per result char): "I" for
|
|
687
736
|
// bytes carried from the INPUT (placeholders from a PRIOR obfuscate() call)
|
|
@@ -937,7 +986,9 @@ export class SecretObfuscator {
|
|
|
937
986
|
// remainder bytes that merely look sentinel-shaped (e.g. `ZZZZ`) cannot equal
|
|
938
987
|
// the marker and are still redacted instead of passed through.
|
|
939
988
|
const replacement =
|
|
940
|
-
chunk.length <= 2
|
|
989
|
+
chunk.length <= 2
|
|
990
|
+
? "Z".repeat(chunk.length)
|
|
991
|
+
: `ZZ${buildKeyedReplacementRun(this.#getKey(), chunk.length - 2)}`;
|
|
941
992
|
this.#generatedReplaceChunks.add(replacement);
|
|
942
993
|
return replacement;
|
|
943
994
|
}
|
|
@@ -1023,7 +1074,9 @@ export class SecretObfuscator {
|
|
|
1023
1074
|
// the ordinary keyed-run fallback #generateReplacement already uses.
|
|
1024
1075
|
replacement =
|
|
1025
1076
|
stable ??
|
|
1026
|
-
(value.length <= 2
|
|
1077
|
+
(value.length <= 2
|
|
1078
|
+
? buildKeyedReplacementRun(this.#getKey(), value.length)
|
|
1079
|
+
: this.#generateReplacement(value));
|
|
1027
1080
|
regex.lastIndex = 0;
|
|
1028
1081
|
}
|
|
1029
1082
|
this.#generatedReplaceChunks.add(replacement);
|
|
@@ -1183,7 +1236,9 @@ export class SecretObfuscator {
|
|
|
1183
1236
|
|
|
1184
1237
|
for (let attempt = 0; ; attempt++) {
|
|
1185
1238
|
const base =
|
|
1186
|
-
attempt === 0
|
|
1239
|
+
attempt === 0
|
|
1240
|
+
? buildHashBase(this.#getKey(), baseKey)
|
|
1241
|
+
: buildHashBase(this.#getKey(), `${baseKey}\0${attempt}`);
|
|
1187
1242
|
const owner = this.#placeholderBaseOwners.get(base);
|
|
1188
1243
|
if (owner !== undefined && owner !== baseKey) continue;
|
|
1189
1244
|
this.#placeholderBaseOwners.set(base, baseKey);
|
|
@@ -1195,7 +1250,7 @@ export class SecretObfuscator {
|
|
|
1195
1250
|
#reserveFallbackPlaceholderBase(baseKey: string, startAttempt: number): string {
|
|
1196
1251
|
for (let attempt = startAttempt; ; attempt++) {
|
|
1197
1252
|
const owner = `${baseKey}\0collision\0${attempt}`;
|
|
1198
|
-
const base = buildHashBase(this.#
|
|
1253
|
+
const base = buildHashBase(this.#getKey(), `${baseKey}\0collision\0${attempt}`);
|
|
1199
1254
|
if (this.#placeholderBaseOwners.has(base)) continue;
|
|
1200
1255
|
this.#placeholderBaseOwners.set(base, owner);
|
|
1201
1256
|
return base;
|
|
@@ -35,6 +35,7 @@ import type { FileSlashCommand } from "../extensibility/slash-commands";
|
|
|
35
35
|
import type { SecretObfuscator } from "../secrets/obfuscator";
|
|
36
36
|
import type { ConfiguredThinkingLevel } from "../thinking";
|
|
37
37
|
import type { XdevState } from "../tools/xdev";
|
|
38
|
+
import type { CodexAutoRedeemCoordinator } from "./codex-auto-reset";
|
|
38
39
|
import type { SessionManager } from "./session-manager";
|
|
39
40
|
|
|
40
41
|
/** Maximum time the interactive shutdown path waits for Mnemopi consolidation. */
|
|
@@ -173,6 +174,8 @@ export interface AgentSessionConfig {
|
|
|
173
174
|
initialAdvisorCosts?: ReadonlyMap<string, number>;
|
|
174
175
|
/** Prefer websocket transport for OpenAI Codex requests when supported. */
|
|
175
176
|
preferWebsockets?: boolean;
|
|
177
|
+
/** Codex saved-reset coordinator; defaults to the process-wide singleton so concurrent sessions can't double-spend. Inject a fresh one in tests. */
|
|
178
|
+
codexResetCoordinator?: CodexAutoRedeemCoordinator;
|
|
176
179
|
/** Provider payload hook used by the active session request path. */
|
|
177
180
|
onPayload?: SimpleStreamOptions["onPayload"];
|
|
178
181
|
/** Provider response hook used by the active session request path. */
|
|
@@ -184,7 +187,10 @@ export interface AgentSessionConfig {
|
|
|
184
187
|
/** Current session message-to-LLM conversion pipeline. */
|
|
185
188
|
convertToLlm?: (messages: AgentMessage[]) => Message[] | Promise<Message[]>;
|
|
186
189
|
/** System prompt builder that can consider tool availability. */
|
|
187
|
-
rebuildSystemPrompt?: (
|
|
190
|
+
rebuildSystemPrompt?: (
|
|
191
|
+
toolNames: string[],
|
|
192
|
+
tools: Map<string, AgentTool>,
|
|
193
|
+
) => Promise<{ systemPrompt: string[]; xdevCatalogNames?: readonly string[] }>;
|
|
188
194
|
/** Local calendar date provider used by prompt-cache invalidation. */
|
|
189
195
|
getLocalCalendarDate?: () => string;
|
|
190
196
|
/** Tools mounted under `xd://`, for `/tools` display. */
|