@xopcai/xopc 0.0.201 → 0.0.203
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/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{activity-store-DOhnAQOQ.js → activity-store-SZAay-ze.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-D8Cw0Mve.js → agent-avatar-dicebear-DPLJtxoS.js} +1 -1
- package/dist/gateway/static/root/assets/agents-DyjRuuRa.js +68 -0
- package/dist/gateway/static/root/assets/{api-E9Bk-Idp.js → api-C1cSt2ru.js} +1 -1
- package/dist/gateway/static/root/assets/{app-management-settings-panel-BubrReXg.js → app-management-settings-panel-B3PCMqor.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-BY5Az2p4.js → appearance-settings-txSx3REK.js} +1 -1
- package/dist/gateway/static/root/assets/apps-page-BEhBaooZ.js +1 -0
- package/dist/gateway/static/root/assets/{archive-plugin-BY8843mB.js → archive-plugin-B2VVc17j.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-DG45KScf.js → attachment-load-Crk7x8qR.js} +1 -1
- package/dist/gateway/static/root/assets/automations-page-BUC6FL6O.js +6 -0
- package/dist/gateway/static/root/assets/{binary-plugins-DGIF2ZAv.js → binary-plugins-CLlJsq5D.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-C1Qktr1g.js → block-editor-cJ5lVNAG.js} +46 -46
- package/dist/gateway/static/root/assets/browser-settings-page-Bq2m4VpX.js +8 -0
- package/dist/gateway/static/root/assets/{browser-workflow-inputs-D_jnndyz.js → browser-workflow-inputs-WRiFG88u.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-workflows-page-DyIRvPaq.js → browser-workflows-page-BHLdVoOC.js} +2 -2
- package/dist/gateway/static/root/assets/capabilities-settings-panel-BiQbVvYU.js +7 -0
- package/dist/gateway/static/root/assets/{capability-presets-api--tz0mGLY.js → capability-presets-api-DmElfgC9.js} +1 -1
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-CYlZQpLY.js +3 -0
- package/dist/gateway/static/root/assets/{channel-recipient-api-CkOKF9nc.js → channel-recipient-api-BUEOtPft.js} +1 -1
- package/dist/gateway/static/root/assets/channels-settings-DlDiCMhO.js +1 -0
- package/dist/gateway/static/root/assets/channels-status-swr-Dm_-934M.js +1 -0
- package/dist/gateway/static/root/assets/{connectors-api-FBOe9SWC.js → connectors-api-BCdn44_o.js} +1 -1
- package/dist/gateway/static/root/assets/connectors-page-Ce-eaPHw.js +2 -0
- package/dist/gateway/static/root/assets/cron-expression-D2hF3t68.js +2 -0
- package/dist/gateway/static/root/assets/{date-picker-Dtn7e6TW.js → date-picker-Dvjoq2mB.js} +1 -1
- package/dist/gateway/static/root/assets/dependency-picker-zv1OIhk3.js +1 -0
- package/dist/gateway/static/root/assets/desktop-pet-DJZhKLTb.js +1 -0
- package/dist/gateway/static/root/assets/{desktop-pet-settings-FDmaty3P.js → desktop-pet-settings-B3ubM-eY.js} +1 -1
- package/dist/gateway/static/root/assets/{directory-picker-path-field-X2e8xn8j.js → directory-picker-path-field-BZEJIWOT.js} +1 -1
- package/dist/gateway/static/root/assets/download-Dk-ZjtO6.js +1 -0
- package/dist/gateway/static/root/assets/{extension-debug-page-Dx4MBfga.js → extension-debug-page-Bvq3E1A1.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-Bf_FEDhM.js → extension-page-CuCJjWNm.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-CrOgZxkf.js → extension-settings-page-CpTk_ajr.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-api-CZ4r-7qT.js → gateway-config-api-C2yM6qj7.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-swr-Cv2ywpdb.js → gateway-config-swr-CN-JHMAh.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-settings-DpWmVi0w.js → gateway-settings-COwi--2Q.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-startup-retry-undf-k5g.js → gateway-startup-retry-Dq1RyCjz.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-settings-DSQZhSvT.js → heartbeat-settings-Dc6H4VGF.js} +1 -1
- package/dist/gateway/static/root/assets/home-page-BB3RGxwm.js +3 -0
- package/dist/gateway/static/root/assets/index-CPTLS-Zn.js +85 -0
- package/dist/gateway/static/root/assets/index-Dm-cvIpl.css +2 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-Bc3Quw42.js → keyboard-shortcuts-settings-cue30Z5p.js} +1 -1
- package/dist/gateway/static/root/assets/local-app-workbench-page-DfO6FOB9.js +2 -0
- package/dist/gateway/static/root/assets/local-apps-page-DKQSCsa0.js +1 -0
- package/dist/gateway/static/root/assets/{locale-store-CnamlR07.js → locale-store-81Gv0wwo.js} +1 -1
- package/dist/gateway/static/root/assets/logs-page-DODbODyl.js +2 -0
- package/dist/gateway/static/root/assets/{management-settings-CJmcaVje.js → management-settings--4H26QR-.js} +1 -1
- package/dist/gateway/static/root/assets/{markdown-split-AcuFp0TH.js → markdown-split-BTvpyjAi.js} +1 -1
- package/dist/gateway/static/root/assets/markdown-view-B12xeflb.css +1 -0
- package/dist/gateway/static/root/assets/markdown-view-B4iBrVWK.js +69 -0
- package/dist/gateway/static/root/assets/{media-plugins-BvFgsiJL.js → media-plugins-DrejWl8t.js} +1 -1
- package/dist/gateway/static/root/assets/messages-ELK_1h2D.js +3 -0
- package/dist/gateway/static/root/assets/{notes-page-IW7IQ9nJ.js → notes-page-DfSzeQKd.js} +1 -1
- package/dist/gateway/static/root/assets/notes-workbench-DxL4GhwV.js +4 -0
- package/dist/gateway/static/root/assets/plus-Q2yW6OE7.js +1 -0
- package/dist/gateway/static/root/assets/preview-open-alternatives-yjh1_7K3.js +1 -0
- package/dist/gateway/static/root/assets/{product-open-page-CWEzLykF.js → product-open-page-DopWBJLa.js} +1 -1
- package/dist/gateway/static/root/assets/project-detail-page-Bl3LJHZB.js +1 -0
- package/dist/gateway/static/root/assets/projects-page-DStOErTC.js +1 -0
- package/dist/gateway/static/root/assets/remote-access-hub-C_hh9ztK.js +1 -0
- package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-DAohEZrX.js → runtime-tools-settings-panel-_dHhbYV2.js} +2 -2
- package/dist/gateway/static/root/assets/{schema-form-C_OBW8A5.js → schema-form-DZhORvJZ.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-DDY21KYw.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-BJrHIcw8.js +1 -0
- package/dist/gateway/static/root/assets/{settings-form-section-C7w2WS-A.js → settings-form-section-Du5eAgFS.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-loading-skeleton-zwibve4m.js → settings-loading-skeleton-DWS27sv1.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-B9W03dSW.js → settings-page-DPD0MwoN.js} +1 -1
- package/dist/gateway/static/root/assets/setup-status-panel-CnXN4LH_.js +1 -0
- package/dist/gateway/static/root/assets/share-preview-page-D6HmH2ga.js +2 -0
- package/dist/gateway/static/root/assets/shares-settings-U3lvtO3Y.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-B6GiZgZU.js +1 -0
- package/dist/gateway/static/root/assets/skills-page-XVO7d3fh.js +2 -0
- package/dist/gateway/static/root/assets/{skills-page.utils-D9QT5cxj.js → skills-page.utils-DwxIpWrI.js} +1 -1
- package/dist/gateway/static/root/assets/{system-settings-panel-c1HVtv1Y.js → system-settings-panel-BmKbxR8_.js} +1 -1
- package/dist/gateway/static/root/assets/task-detail-page-DA6ItYrc.js +1 -0
- package/dist/gateway/static/root/assets/{theme-store-BDyUTObE.js → theme-store-hOF81tUQ.js} +1 -1
- package/dist/gateway/static/root/assets/{use-autosave-D6UkH_Ga.js → use-autosave-GJfDpf66.js} +1 -1
- package/dist/gateway/static/root/assets/user-context-page-CQ1QrRE6.js +1 -0
- package/dist/gateway/static/root/assets/{voice-api-key-field-BOxkKbGh.js → voice-api-key-field-B0zve9EE.js} +1 -1
- package/dist/gateway/static/root/assets/work-discovery-overlay-DrIbHnBz.js +1 -0
- package/dist/gateway/static/root/assets/work-discovery-page-CQW8pPec.js +1 -0
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-ZA3bP65I.js → workflow-run-setup-panel-D-_zQxim.js} +2 -2
- package/dist/gateway/static/root/assets/workflows-page-O21l_f5c.js +5 -0
- package/dist/gateway/static/root/assets/{working-directory-picker-modal-BcyiDXU3.js → working-directory-picker-modal-5xYx48tV.js} +1 -1
- package/dist/gateway/static/root/assets/x-B_N4OPjy.js +1 -0
- package/dist/gateway/static/root/index.html +19 -14
- package/dist/package.js +1 -1
- package/dist/src/agent/agent-manager.js +2 -1
- package/dist/src/agent/embedded/run-for-session.js +3 -2
- package/dist/src/agent/embedded/run-turn.js +29 -16
- package/dist/src/agent/embedded/types.d.ts +2 -2
- package/dist/src/agent/embedded/xopc-stream-bridge.d.ts +2 -1
- package/dist/src/agent/embedded/xopc-stream-bridge.js +2 -2
- package/dist/src/agent/memory/context-budget.d.ts +6 -1
- package/dist/src/agent/memory/context-budget.js +94 -2
- package/dist/src/agent/prompt/cache-boundary.d.ts +2 -0
- package/dist/src/agent/prompt/cache-boundary.js +10 -1
- package/dist/src/agent/prompt/sections/project-context.d.ts +1 -4
- package/dist/src/agent/prompt/sections/project-context.js +6 -13
- package/dist/src/agent/prompt/service-prompt-builder.js +2 -1
- package/dist/src/agent/prompt/system-prompt.d.ts +1 -3
- package/dist/src/agent/prompt/system-prompt.js +6 -15
- package/dist/src/agent/tools/cache-stability.d.ts +3 -0
- package/dist/src/agent/tools/cache-stability.js +19 -0
- package/dist/src/agent/tools/factory.js +2 -1
- package/dist/src/agent-manifest/schema.d.ts +40 -20
- package/dist/src/agent-manifest/schema.js +4 -5
- package/dist/src/config/schema.d.ts +40 -20
- package/dist/src/extensions/types/providers.d.ts +0 -2
- package/dist/src/gateway/hono/lib/config-payload.d.ts +4 -1
- package/dist/src/gateway/hono/oauth-async.d.ts +1 -0
- package/dist/src/gateway/hono/oauth-async.js +14 -2
- package/dist/src/providers/extension-stream-bridge.js +0 -2
- package/dist/src/providers/google-managed-prompt-cache.d.ts +14 -0
- package/dist/src/providers/google-managed-prompt-cache.js +125 -0
- package/dist/src/providers/prompt-cache-fingerprint.d.ts +4 -0
- package/dist/src/providers/prompt-cache-fingerprint.js +44 -0
- package/dist/src/providers/prompt-cache-lifecycle.d.ts +9 -0
- package/dist/src/providers/prompt-cache-lifecycle.js +31 -0
- package/dist/src/providers/prompt-cache-observability.d.ts +20 -0
- package/dist/src/providers/prompt-cache-observability.js +44 -0
- package/dist/src/providers/prompt-cache-payload.d.ts +10 -3
- package/dist/src/providers/prompt-cache-payload.js +204 -31
- package/dist/src/providers/prompt-cache-plan.d.ts +15 -0
- package/dist/src/providers/prompt-cache-plan.js +29 -0
- package/package.json +3 -3
- package/dist/gateway/static/root/assets/agents-ANavi648.js +0 -68
- package/dist/gateway/static/root/assets/apps-page-CFh9MBxc.js +0 -1
- package/dist/gateway/static/root/assets/automations-page-751CeNI3.js +0 -6
- package/dist/gateway/static/root/assets/browser-settings-page-CGAWs-TT.js +0 -8
- package/dist/gateway/static/root/assets/capabilities-settings-panel-CghUK68J.js +0 -7
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-XsTfkvUn.js +0 -3
- package/dist/gateway/static/root/assets/channels-settings-DkoAZZMe.js +0 -1
- package/dist/gateway/static/root/assets/channels-status-swr-DNDvMWpl.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-e4vekkAu.js +0 -2
- package/dist/gateway/static/root/assets/cron-expression-Q4QaYjpI.js +0 -2
- package/dist/gateway/static/root/assets/dependency-picker-Br6nPaM6.js +0 -1
- package/dist/gateway/static/root/assets/desktop-pet-DzTM6aH8.js +0 -1
- package/dist/gateway/static/root/assets/home-page-L0uHiu0T.js +0 -3
- package/dist/gateway/static/root/assets/index-Bp8I_eX4.css +0 -1
- package/dist/gateway/static/root/assets/index-DRrjYYzZ.js +0 -152
- package/dist/gateway/static/root/assets/local-app-workbench-page-Copi3sdP.js +0 -2
- package/dist/gateway/static/root/assets/local-apps-page-CP9X_vtz.js +0 -1
- package/dist/gateway/static/root/assets/logs-page-DMKq6e29.js +0 -2
- package/dist/gateway/static/root/assets/messages-D7kOD_o_.js +0 -3
- package/dist/gateway/static/root/assets/notes-workbench-Zb8U0anO.js +0 -4
- package/dist/gateway/static/root/assets/preview-open-alternatives-C-b34dgQ.js +0 -1
- package/dist/gateway/static/root/assets/project-detail-page-BuIbLnan.js +0 -1
- package/dist/gateway/static/root/assets/projects-page-BwUfRqNV.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-BTaXxcEi.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-BnHoTCD3.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-BTB_fxjt.js +0 -1
- package/dist/gateway/static/root/assets/setup-status-panel-C8Hec2Af.js +0 -1
- package/dist/gateway/static/root/assets/share-preview-page-6e3ks2be.js +0 -2
- package/dist/gateway/static/root/assets/shares-settings-Bwc5HfLI.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-D7kunhqy.js +0 -1
- package/dist/gateway/static/root/assets/skills-page-WgkaNzpc.js +0 -2
- package/dist/gateway/static/root/assets/task-detail-page-CjP-6WVU.js +0 -1
- package/dist/gateway/static/root/assets/user-context-page-D8ZhPDNF.js +0 -1
- package/dist/gateway/static/root/assets/work-discovery-overlay-Dd19yK9t.js +0 -1
- package/dist/gateway/static/root/assets/work-discovery-page-DUkdJl1e.js +0 -1
- package/dist/gateway/static/root/assets/workflows-page-C2b5W5bw.js +0 -5
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import { createLogger } from "../../utils/logger/index.js";
|
|
2
2
|
import { init_logger } from "../../utils/logger.js";
|
|
3
3
|
import { resolveAgentTurnTimeoutMs, runAgentTurnWithTimeout } from "../orchestration/run-agent-turn-with-timeout.js";
|
|
4
|
+
import { resolvePromptCachePolicy } from "../../providers/prompt-cache-plan.js";
|
|
4
5
|
import { wrapStreamFnForXopcExtensions } from "./xopc-stream-bridge.js";
|
|
5
6
|
import { acquireEmbeddedSessionRunner } from "./session-runner.js";
|
|
6
7
|
import { getAssistantTurnErrorMessage, isAssistantTurnAborted, isAssistantTurnFailed, maybeRetryTurnAfterTransientLlmFailure, stripTrailingErrorAssistantMessages } from "../orchestration/llm-turn-retry.js";
|
|
7
|
-
import {
|
|
8
|
+
import { projectContextForModel } from "../memory/context-budget.js";
|
|
8
9
|
import { abortEmbeddedRun, queueEmbeddedSteer, registerEmbeddedRun, unregisterEmbeddedRun } from "./runs.js";
|
|
9
10
|
import { lastAssistantPlainText, subscribeEmbeddedSessionEvents } from "./subscribe-session.js";
|
|
10
11
|
import { detectToolLoops } from "../orchestration/loop-guard.js";
|
|
11
12
|
import { tryApplySessionTranscriptHygiene } from "../transcript/transcript-hygiene.js";
|
|
12
13
|
import { createSqliteTranscriptRuntime } from "./transcript-runtime.js";
|
|
13
14
|
import { isContextOverflowError } from "../orchestration/context-overflow.js";
|
|
15
|
+
import { buildPromptCacheSnapshot, observePromptCacheSnapshot } from "../../providers/prompt-cache-observability.js";
|
|
16
|
+
import { isPromptCacheExpired, recordPromptCacheTouch } from "../../providers/prompt-cache-lifecycle.js";
|
|
14
17
|
//#region src/agent/embedded/run-turn.ts
|
|
15
18
|
init_logger();
|
|
16
19
|
const log = createLogger("EmbeddedRun");
|
|
@@ -98,6 +101,7 @@ async function runXopcEmbeddedTurn(params) {
|
|
|
98
101
|
const { sessionKey, runId, userMessage, model, tools, systemPrompt, thinkingLevel, workspaceDir, sessionStore, onEvent } = params;
|
|
99
102
|
const timeoutMs = params.timeoutMs || resolveAgentTurnTimeoutMs();
|
|
100
103
|
const resolvedModel = requireEmbeddedModel(model, params.modelRef);
|
|
104
|
+
const promptCachePolicy = resolvePromptCachePolicy(params.promptCachePolicy);
|
|
101
105
|
const transcriptRuntime = params.transcriptRuntime ?? (sessionStore ? await createSqliteTranscriptRuntime({
|
|
102
106
|
sessionKey,
|
|
103
107
|
sessionStore
|
|
@@ -119,7 +123,7 @@ async function runXopcEmbeddedTurn(params) {
|
|
|
119
123
|
});
|
|
120
124
|
const { session, reused } = runner;
|
|
121
125
|
runner.piSm.setActiveTurnId?.(runId);
|
|
122
|
-
const streamFnWithXopcExtensions = wrapStreamFnForXopcExtensions(session.agent.streamFunction);
|
|
126
|
+
const streamFnWithXopcExtensions = wrapStreamFnForXopcExtensions(session.agent.streamFunction, params.promptCachePolicy);
|
|
123
127
|
const loggingStreamFn = (streamModel, context, options) => {
|
|
124
128
|
const sourceMessages = [...context.messages];
|
|
125
129
|
const hygienicMessages = tryApplySessionTranscriptHygiene(sourceMessages, streamModel);
|
|
@@ -141,26 +145,35 @@ async function runXopcEmbeddedTurn(params) {
|
|
|
141
145
|
tools: contextTools
|
|
142
146
|
};
|
|
143
147
|
}
|
|
144
|
-
const
|
|
148
|
+
const projection = projectContextForModel({
|
|
145
149
|
messages: effectiveContext.messages,
|
|
146
150
|
contextWindow: streamModel.contextWindow ?? 128e3,
|
|
147
151
|
systemPrompt: effectiveContext.systemPrompt,
|
|
148
152
|
tools: effectiveContext.tools,
|
|
149
|
-
canCompact: false
|
|
153
|
+
canCompact: false,
|
|
154
|
+
reason: isPromptCacheExpired(sessionKey, streamModel, promptCachePolicy) ? "cache_expired" : "normal"
|
|
150
155
|
});
|
|
151
|
-
if (
|
|
156
|
+
if (projection.prunedToolResults > 0 || projection.prunedImages > 0) {
|
|
152
157
|
effectiveContext = {
|
|
153
158
|
...effectiveContext,
|
|
154
|
-
messages:
|
|
159
|
+
messages: projection.messages
|
|
155
160
|
};
|
|
156
161
|
log.warn({
|
|
157
162
|
sessionKey,
|
|
158
163
|
runId,
|
|
159
|
-
prunedToolResults:
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
164
|
+
prunedToolResults: projection.prunedToolResults,
|
|
165
|
+
prunedImages: projection.prunedImages,
|
|
166
|
+
estimatedTokens: projection.evaluation.estimatedTokens,
|
|
167
|
+
hardLimitTokens: projection.evaluation.hardLimitTokens
|
|
168
|
+
}, "Projected a smaller provider context");
|
|
163
169
|
}
|
|
170
|
+
const promptCacheSnapshot = buildPromptCacheSnapshot({
|
|
171
|
+
model: streamModel,
|
|
172
|
+
systemPrompt: effectiveContext.systemPrompt,
|
|
173
|
+
tools: effectiveContext.tools,
|
|
174
|
+
reasoning: options?.reasoning
|
|
175
|
+
});
|
|
176
|
+
const promptCacheChanges = observePromptCacheSnapshot(sessionKey, promptCacheSnapshot);
|
|
164
177
|
log.debug({
|
|
165
178
|
sessionKey,
|
|
166
179
|
runId,
|
|
@@ -173,20 +186,20 @@ async function runXopcEmbeddedTurn(params) {
|
|
|
173
186
|
lastUserMessagePreview: getLastUserMessagePreview(effectiveContext.messages),
|
|
174
187
|
loopWarningInjected: !!loopGuard.injection,
|
|
175
188
|
hiddenToolCount: loopGuard.hiddenTools.size,
|
|
189
|
+
promptCache: promptCacheSnapshot,
|
|
190
|
+
promptCacheChanges,
|
|
176
191
|
...process.env.XOPC_LOG_LLM_PAYLOAD === "true" ? { effectiveContext: {
|
|
177
192
|
systemPrompt: effectiveContext.systemPrompt,
|
|
178
193
|
messages: effectiveContext.messages,
|
|
179
194
|
tools: effectiveContext.tools
|
|
180
195
|
} } : {}
|
|
181
196
|
}, "Sending messages to AI");
|
|
182
|
-
return streamFnWithXopcExtensions(streamModel, effectiveContext, {
|
|
183
|
-
...options,
|
|
184
|
-
...params.cacheRetention ? { cacheRetention: params.cacheRetention } : {}
|
|
185
|
-
});
|
|
197
|
+
return streamFnWithXopcExtensions(streamModel, effectiveContext, { ...options });
|
|
186
198
|
};
|
|
187
199
|
session.agent.streamFunction = loggingStreamFn;
|
|
188
|
-
|
|
189
|
-
|
|
200
|
+
unsubscribe = subscribeEmbeddedSessionEvents(session, (event) => {
|
|
201
|
+
if (event.type === "message_end" && event.message.role === "assistant") recordPromptCacheTouch(sessionKey, resolvedModel, event.message.usage);
|
|
202
|
+
onEvent?.({
|
|
190
203
|
...event,
|
|
191
204
|
runId
|
|
192
205
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AgentMessage, AgentToolResult, ThinkingLevel } from '@earendil-works/pi-agent-core';
|
|
2
|
-
import type { AssistantMessageEvent, Model, Api,
|
|
2
|
+
import type { AssistantMessageEvent, Model, Api, ImageContent } from '@earendil-works/pi-ai';
|
|
3
3
|
import type { SessionStore } from '../../session/store.js';
|
|
4
4
|
import type { EmbeddedTranscriptRuntime } from './transcript-runtime.js';
|
|
5
5
|
export type EmbeddedStreamEvent = {
|
|
@@ -92,7 +92,7 @@ export type RunXopcEmbeddedTurnParams = {
|
|
|
92
92
|
tools: import('@earendil-works/pi-agent-core').AgentTool[];
|
|
93
93
|
systemPrompt: string;
|
|
94
94
|
thinkingLevel?: ThinkingLevel;
|
|
95
|
-
|
|
95
|
+
promptCachePolicy?: import('../../providers/prompt-cache-plan.js').PromptCachePolicy;
|
|
96
96
|
workspaceDir: string;
|
|
97
97
|
sessionStore?: SessionStore;
|
|
98
98
|
transcriptRuntime?: EmbeddedTranscriptRuntime;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { StreamFn } from '@earendil-works/pi-agent-core';
|
|
2
|
+
import type { PromptCachePolicy } from '../../providers/prompt-cache-plan.js';
|
|
2
3
|
/**
|
|
3
4
|
* pi-coding-agent's default {@link createAgentSession} streamFn always routes through
|
|
4
5
|
* pi-ai's HTTP `streamSimple`, so xopc plugin providers (whose models carry the sentinel
|
|
@@ -8,4 +9,4 @@ import type { StreamFn } from '@earendil-works/pi-agent-core';
|
|
|
8
9
|
*
|
|
9
10
|
* Use this by reassigning {@link Agent.streamFunction} after `createAgentSession` returns.
|
|
10
11
|
*/
|
|
11
|
-
export declare function wrapStreamFnForXopcExtensions(originalStreamFn: StreamFn): StreamFn;
|
|
12
|
+
export declare function wrapStreamFnForXopcExtensions(originalStreamFn: StreamFn, promptCachePolicy?: PromptCachePolicy): StreamFn;
|
|
@@ -13,11 +13,11 @@ init_providers();
|
|
|
13
13
|
*
|
|
14
14
|
* Use this by reassigning {@link Agent.streamFunction} after `createAgentSession` returns.
|
|
15
15
|
*/
|
|
16
|
-
function wrapStreamFnForXopcExtensions(originalStreamFn) {
|
|
16
|
+
function wrapStreamFnForXopcExtensions(originalStreamFn, promptCachePolicy) {
|
|
17
17
|
const extensionStreamFn = createExtensionAwareStreamFn();
|
|
18
18
|
return ((model, context, options) => {
|
|
19
19
|
if (model.baseUrl === "extension://provider-plugin") return extensionStreamFn(model, preparePromptCacheContext(model, context), options);
|
|
20
|
-
return originalStreamFn(model, preparePromptCacheContext(model, context), withPromptCachePayloadTransform(model, context, options));
|
|
20
|
+
return originalStreamFn(model, preparePromptCacheContext(model, context), withPromptCachePayloadTransform(model, context, options, promptCachePolicy));
|
|
21
21
|
});
|
|
22
22
|
}
|
|
23
23
|
//#endregion
|
|
@@ -16,6 +16,7 @@ export interface ContextBudgetInput {
|
|
|
16
16
|
minToolResultKeepChars?: number;
|
|
17
17
|
canCompact?: boolean;
|
|
18
18
|
}
|
|
19
|
+
export type ContextProjectionReason = 'normal' | 'cache_expired' | 'hard_limit';
|
|
19
20
|
export interface ContextBudgetEvaluation {
|
|
20
21
|
route: ContextBudgetRoute;
|
|
21
22
|
estimatedTokens: number;
|
|
@@ -33,8 +34,12 @@ export declare function estimateTextTokens(text: string): number;
|
|
|
33
34
|
export declare function estimateMessageTokens(message: AgentMessage): number;
|
|
34
35
|
export declare function estimateMessagesTokens(messages: readonly AgentMessage[]): number;
|
|
35
36
|
export declare function evaluateContextBudget(input: ContextBudgetInput): ContextBudgetEvaluation;
|
|
36
|
-
export declare function
|
|
37
|
+
export declare function projectContextForModel(input: ContextBudgetInput & {
|
|
38
|
+
reason: ContextProjectionReason;
|
|
39
|
+
recentTurns?: number;
|
|
40
|
+
}): {
|
|
37
41
|
messages: AgentMessage[];
|
|
38
42
|
evaluation: ContextBudgetEvaluation;
|
|
39
43
|
prunedToolResults: number;
|
|
44
|
+
prunedImages: number;
|
|
40
45
|
};
|
|
@@ -73,7 +73,7 @@ function evaluateContextBudget(input) {
|
|
|
73
73
|
usagePercent: estimatedTokens / contextWindow
|
|
74
74
|
};
|
|
75
75
|
}
|
|
76
|
-
function
|
|
76
|
+
function truncateToolResultsToFit(input) {
|
|
77
77
|
const initial = evaluateContextBudget(input);
|
|
78
78
|
if (initial.estimatedTokens <= initial.hardLimitTokens || initial.reducibleToolResultTokens === 0) return {
|
|
79
79
|
messages: input.messages,
|
|
@@ -101,5 +101,97 @@ function pruneToolResultsToFit(input) {
|
|
|
101
101
|
prunedToolResults
|
|
102
102
|
};
|
|
103
103
|
}
|
|
104
|
+
function protectedRecentTurnStart(messages, recentTurns) {
|
|
105
|
+
let remaining = recentTurns;
|
|
106
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
107
|
+
if (messages[index].role !== "assistant") continue;
|
|
108
|
+
remaining -= 1;
|
|
109
|
+
if (remaining === 0) return index;
|
|
110
|
+
}
|
|
111
|
+
return 0;
|
|
112
|
+
}
|
|
113
|
+
function replaceOldImages(message) {
|
|
114
|
+
const content = message.content;
|
|
115
|
+
if (!Array.isArray(content)) return {
|
|
116
|
+
message,
|
|
117
|
+
removed: 0
|
|
118
|
+
};
|
|
119
|
+
let removed = 0;
|
|
120
|
+
const nextContent = content.map((block) => {
|
|
121
|
+
if (!block || typeof block !== "object" || block.type !== "image") return block;
|
|
122
|
+
removed += 1;
|
|
123
|
+
return {
|
|
124
|
+
type: "text",
|
|
125
|
+
text: "[Earlier image omitted from the model context.]"
|
|
126
|
+
};
|
|
127
|
+
});
|
|
128
|
+
return removed > 0 ? {
|
|
129
|
+
message: {
|
|
130
|
+
...message,
|
|
131
|
+
content: nextContent
|
|
132
|
+
},
|
|
133
|
+
removed
|
|
134
|
+
} : {
|
|
135
|
+
message,
|
|
136
|
+
removed: 0
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function projectContextForModel(input) {
|
|
140
|
+
const initial = evaluateContextBudget(input);
|
|
141
|
+
const mustFit = input.reason === "hard_limit" || initial.estimatedTokens > initial.hardLimitTokens;
|
|
142
|
+
if (input.reason !== "cache_expired" && !mustFit) return {
|
|
143
|
+
messages: input.messages,
|
|
144
|
+
evaluation: initial,
|
|
145
|
+
prunedToolResults: 0,
|
|
146
|
+
prunedImages: 0
|
|
147
|
+
};
|
|
148
|
+
if (input.reason === "cache_expired" && initial.usagePercent < .3 && !mustFit) return {
|
|
149
|
+
messages: input.messages,
|
|
150
|
+
evaluation: initial,
|
|
151
|
+
prunedToolResults: 0,
|
|
152
|
+
prunedImages: 0
|
|
153
|
+
};
|
|
154
|
+
const protectedStart = protectedRecentTurnStart(input.messages, input.recentTurns ?? 3);
|
|
155
|
+
let prunedToolResults = 0;
|
|
156
|
+
let prunedImages = 0;
|
|
157
|
+
let messages = input.messages.map((message, index) => {
|
|
158
|
+
if (index >= protectedStart) return message;
|
|
159
|
+
const imageProjection = replaceOldImages(message);
|
|
160
|
+
prunedImages += imageProjection.removed;
|
|
161
|
+
if (getToolResultTextLength(imageProjection.message) <= 3e3) return imageProjection.message;
|
|
162
|
+
prunedToolResults += 1;
|
|
163
|
+
return truncateToolResultMessage(imageProjection.message, 3e3, { minKeepChars: 1500 });
|
|
164
|
+
});
|
|
165
|
+
let evaluation = evaluateContextBudget({
|
|
166
|
+
...input,
|
|
167
|
+
messages
|
|
168
|
+
});
|
|
169
|
+
if (input.reason === "cache_expired" && evaluation.usagePercent > .5) {
|
|
170
|
+
messages = messages.map((message, index) => {
|
|
171
|
+
if (index >= protectedStart || getToolResultTextLength(message) <= 400) return message;
|
|
172
|
+
prunedToolResults += 1;
|
|
173
|
+
return truncateToolResultMessage(message, 400, { minKeepChars: 200 });
|
|
174
|
+
});
|
|
175
|
+
evaluation = evaluateContextBudget({
|
|
176
|
+
...input,
|
|
177
|
+
messages
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
if (evaluation.estimatedTokens > evaluation.hardLimitTokens) {
|
|
181
|
+
const fitted = truncateToolResultsToFit({
|
|
182
|
+
...input,
|
|
183
|
+
messages
|
|
184
|
+
});
|
|
185
|
+
messages = fitted.messages;
|
|
186
|
+
prunedToolResults += fitted.prunedToolResults;
|
|
187
|
+
evaluation = fitted.evaluation;
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
messages,
|
|
191
|
+
evaluation,
|
|
192
|
+
prunedToolResults,
|
|
193
|
+
prunedImages
|
|
194
|
+
};
|
|
195
|
+
}
|
|
104
196
|
//#endregion
|
|
105
|
-
export { estimateMessageTokens, estimateMessagesTokens, estimateTextTokens, evaluateContextBudget,
|
|
197
|
+
export { estimateMessageTokens, estimateMessagesTokens, estimateTextTokens, evaluateContextBudget, projectContextForModel };
|
|
@@ -22,3 +22,5 @@ export declare function splitPromptCacheBoundary(text: string): {
|
|
|
22
22
|
* Trims trailing whitespace per line, normalizes line endings, trims overall.
|
|
23
23
|
*/
|
|
24
24
|
export declare function normalizePromptSection(text: string): string;
|
|
25
|
+
/** Add stable content immediately before the cache boundary. */
|
|
26
|
+
export declare function appendStablePromptSection(systemPrompt: string, section: string): string;
|
|
@@ -31,5 +31,14 @@ function splitPromptCacheBoundary(text) {
|
|
|
31
31
|
function normalizePromptSection(text) {
|
|
32
32
|
return text.replace(/\r\n?/g, "\n").replace(/[ \t]+$/gm, "").trim();
|
|
33
33
|
}
|
|
34
|
+
/** Add stable content immediately before the cache boundary. */
|
|
35
|
+
function appendStablePromptSection(systemPrompt, section) {
|
|
36
|
+
const normalizedSection = normalizePromptSection(section);
|
|
37
|
+
if (!normalizedSection) return systemPrompt;
|
|
38
|
+
const split = splitPromptCacheBoundary(systemPrompt);
|
|
39
|
+
if (!split) return [normalizePromptSection(systemPrompt), normalizedSection].filter(Boolean).join("\n\n");
|
|
40
|
+
const stablePrefix = [split.stablePrefix, normalizedSection].filter(Boolean).join("\n\n");
|
|
41
|
+
return split.dynamicSuffix ? `${stablePrefix}${PROMPT_CACHE_BOUNDARY}${split.dynamicSuffix}` : `${stablePrefix}${PROMPT_CACHE_BOUNDARY}`;
|
|
42
|
+
}
|
|
34
43
|
//#endregion
|
|
35
|
-
export { PROMPT_CACHE_BOUNDARY, normalizePromptSection, splitPromptCacheBoundary, stripPromptCacheBoundary };
|
|
44
|
+
export { PROMPT_CACHE_BOUNDARY, appendStablePromptSection, normalizePromptSection, splitPromptCacheBoundary, stripPromptCacheBoundary };
|
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
import type { EmbeddedContextFile } from '../../bootstrap/types.js';
|
|
2
2
|
export declare const CONTEXT_FILE_ORDER: Map<string, number>;
|
|
3
|
-
export declare const DYNAMIC_CONTEXT_FILE_BASENAMES: Set<string>;
|
|
4
3
|
export declare const DEFAULT_HEARTBEAT_PROMPT_CONTEXT_BLOCK = "Default heartbeat prompt:\n`Read HEARTBEAT.md if it exists (workspace context). Follow it strictly. Do not infer or repeat old tasks from prior chats. If nothing needs attention, reply HEARTBEAT_OK.`";
|
|
5
4
|
export declare function normalizeContextFilePath(pathValue: string): string;
|
|
6
5
|
export declare function getContextFileBasename(pathValue: string): string;
|
|
7
|
-
export declare function isDynamicContextFile(pathValue: string): boolean;
|
|
8
6
|
export declare function sanitizeContextFileContentForPrompt(content: string): string;
|
|
9
7
|
export declare function sortContextFilesForPrompt(contextFiles: EmbeddedContextFile[]): EmbeddedContextFile[];
|
|
10
8
|
export declare function buildProjectContextSection(params: {
|
|
11
9
|
files: EmbeddedContextFile[];
|
|
12
10
|
heading: string;
|
|
13
|
-
dynamic: boolean;
|
|
14
11
|
}): string[];
|
|
15
|
-
/**
|
|
12
|
+
/** Heartbeat content belongs to the heartbeat user turn, never the system prompt. */
|
|
16
13
|
export declare function isHeartbeatContextFile(pathValue: string): boolean;
|
|
@@ -9,7 +9,6 @@ const CONTEXT_FILE_ORDER = new Map([
|
|
|
9
9
|
["bootstrap.md", 60],
|
|
10
10
|
["memory.md", 70]
|
|
11
11
|
]);
|
|
12
|
-
const DYNAMIC_CONTEXT_FILE_BASENAMES = new Set(["heartbeat.md"]);
|
|
13
12
|
const DEFAULT_HEARTBEAT_PROMPT_CONTEXT_BLOCK = "Default heartbeat prompt:\n`Read HEARTBEAT.md if it exists (workspace context). Follow it strictly. Do not infer or repeat old tasks from prior chats. If nothing needs attention, reply HEARTBEAT_OK.`";
|
|
14
13
|
function normalizeContextFilePath(pathValue) {
|
|
15
14
|
return pathValue.trim().replace(/\\/g, "/");
|
|
@@ -18,9 +17,6 @@ function getContextFileBasename(pathValue) {
|
|
|
18
17
|
const normalizedPath = normalizeContextFilePath(pathValue);
|
|
19
18
|
return (normalizedPath.split("/").pop() ?? normalizedPath).toLowerCase();
|
|
20
19
|
}
|
|
21
|
-
function isDynamicContextFile(pathValue) {
|
|
22
|
-
return DYNAMIC_CONTEXT_FILE_BASENAMES.has(getContextFileBasename(pathValue));
|
|
23
|
-
}
|
|
24
20
|
function sanitizeContextFileContentForPrompt(content) {
|
|
25
21
|
return content.replaceAll(DEFAULT_HEARTBEAT_PROMPT_CONTEXT_BLOCK, "").replace(/\n{3,}/g, "\n\n");
|
|
26
22
|
}
|
|
@@ -38,19 +34,16 @@ function sortContextFilesForPrompt(contextFiles) {
|
|
|
38
34
|
function buildProjectContextSection(params) {
|
|
39
35
|
if (params.files.length === 0) return [];
|
|
40
36
|
const lines = [params.heading, ""];
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if (hasSoulFile) lines.push("If SOUL.md is present, embody its persona and tone. Avoid stiff, generic replies; follow its guidance unless higher-priority instructions override it.");
|
|
46
|
-
lines.push("");
|
|
47
|
-
}
|
|
37
|
+
const hasSoulFile = params.files.some((file) => getContextFileBasename(file.path) === "soul.md");
|
|
38
|
+
lines.push("The following project context files have been loaded:");
|
|
39
|
+
if (hasSoulFile) lines.push("If SOUL.md is present, embody its persona and tone. Avoid stiff, generic replies; follow its guidance unless higher-priority instructions override it.");
|
|
40
|
+
lines.push("");
|
|
48
41
|
for (const file of params.files) lines.push(`## ${file.path}`, "", sanitizeContextFileContentForPrompt(file.content), "");
|
|
49
42
|
return lines;
|
|
50
43
|
}
|
|
51
|
-
/**
|
|
44
|
+
/** Heartbeat content belongs to the heartbeat user turn, never the system prompt. */
|
|
52
45
|
function isHeartbeatContextFile(pathValue) {
|
|
53
46
|
return getContextFileBasename(pathValue) === DEFAULT_HEARTBEAT_FILENAME.toLowerCase();
|
|
54
47
|
}
|
|
55
48
|
//#endregion
|
|
56
|
-
export { CONTEXT_FILE_ORDER, DEFAULT_HEARTBEAT_PROMPT_CONTEXT_BLOCK,
|
|
49
|
+
export { CONTEXT_FILE_ORDER, DEFAULT_HEARTBEAT_PROMPT_CONTEXT_BLOCK, buildProjectContextSection, getContextFileBasename, isHeartbeatContextFile, normalizeContextFilePath, sanitizeContextFileContentForPrompt, sortContextFilesForPrompt };
|
|
@@ -7,6 +7,7 @@ import { getInteractionState } from "../../storage/sqlite/interaction-state-repo
|
|
|
7
7
|
import { DEFAULT_USER_TRUST_LEVEL } from "../../user-context/trust-policy.js";
|
|
8
8
|
import { getUserTrustPolicy } from "../../storage/sqlite/user-trust-repository.js";
|
|
9
9
|
import "../../storage/sqlite/index.js";
|
|
10
|
+
import { appendStablePromptSection } from "./cache-boundary.js";
|
|
10
11
|
import { resolveSystemPromptBuildParams } from "./system-prompt-params.js";
|
|
11
12
|
import { buildSystemPrompt } from "./system-prompt.js";
|
|
12
13
|
import { resolveResponseLanguageForSession } from "./response-language.js";
|
|
@@ -85,7 +86,7 @@ var SystemPromptBuilder = class {
|
|
|
85
86
|
customInstructions: options.customInstructions
|
|
86
87
|
});
|
|
87
88
|
const skillPrompt = options.skillPromptText !== void 0 ? options.skillPromptText : this.skillManager.getPromptForSkillAllowlist(options.skillAllowlist, options.registeredToolNames);
|
|
88
|
-
const fullPrompt = skillPrompt ?
|
|
89
|
+
const fullPrompt = skillPrompt ? appendStablePromptSection(basePrompt, skillPrompt) : basePrompt;
|
|
89
90
|
log.debug({
|
|
90
91
|
baseLength: basePrompt.length,
|
|
91
92
|
skillLength: skillPrompt?.length || 0,
|
|
@@ -12,7 +12,7 @@ import type { ProviderSystemPromptContribution } from './contribution.js';
|
|
|
12
12
|
import { type RuntimeInfoInput } from './sections/workspace-runtime.js';
|
|
13
13
|
import type { MemoryCitationsMode, PromptMode, SilentReplyPromptMode } from './types.js';
|
|
14
14
|
export type { MemoryCitationsMode } from './types.js';
|
|
15
|
-
export { getContextFileBasename,
|
|
15
|
+
export { getContextFileBasename, sortContextFilesForPrompt, } from './sections/project-context.js';
|
|
16
16
|
export interface SystemPromptOptions {
|
|
17
17
|
contextFiles?: EmbeddedContextFile[];
|
|
18
18
|
promptMode?: PromptMode;
|
|
@@ -21,8 +21,6 @@ export interface SystemPromptOptions {
|
|
|
21
21
|
/** Registered tool names for Tooling section and memory/skills gating. */
|
|
22
22
|
toolNames?: string[];
|
|
23
23
|
toolSummaries?: Record<string, string>;
|
|
24
|
-
/** @deprecated Prefer toolNames — kept for skill-section gating compatibility. */
|
|
25
|
-
availableTools?: string[];
|
|
26
24
|
memoryCitationsMode?: MemoryCitationsMode;
|
|
27
25
|
includeMemorySection?: boolean;
|
|
28
26
|
userTimezone?: string;
|
|
@@ -3,7 +3,7 @@ import { PROMPT_CACHE_BOUNDARY, splitPromptCacheBoundary } from "./cache-boundar
|
|
|
3
3
|
import { buildAestheticSection, buildExecutionBiasSection, buildHumanCollaborationSection, buildProblemSolvingSection, buildSafetySection, buildToolCallStyleSection, buildWorkContinuitySection } from "./sections/behavior.js";
|
|
4
4
|
import { buildExternalMemorySection, buildMemorySection, buildSkillsSection } from "./sections/memory-skills.js";
|
|
5
5
|
import { buildHeartbeatBehaviorSection, buildMessagingSection, buildOutputDirectivesSection, buildSilentRepliesSection, buildTimeSection } from "./sections/messaging-runtime.js";
|
|
6
|
-
import { buildProjectContextSection, getContextFileBasename,
|
|
6
|
+
import { buildProjectContextSection, getContextFileBasename, isHeartbeatContextFile, sortContextFilesForPrompt } from "./sections/project-context.js";
|
|
7
7
|
import { buildToolingSection, hasSkillsTools } from "./sections/tooling.js";
|
|
8
8
|
import { buildRuntimeSection, buildWorkspaceFilesIntroSection, buildWorkspaceSection } from "./sections/workspace-runtime.js";
|
|
9
9
|
import { buildResponseLanguageSection } from "./sections/response-language.js";
|
|
@@ -16,7 +16,7 @@ function joinSections(sections) {
|
|
|
16
16
|
* Build system prompt with bootstrap Project Context integration.
|
|
17
17
|
*/
|
|
18
18
|
function buildSystemPrompt(workspaceDir, options = {}) {
|
|
19
|
-
const { contextFiles = [], promptMode = "full", heartbeatEnabled = false, heartbeatPrompt, toolNames
|
|
19
|
+
const { contextFiles = [], promptMode = "full", heartbeatEnabled = false, heartbeatPrompt, toolNames = [], toolSummaries, memoryCitationsMode = "on", includeMemorySection, userTimezone, runtime, agentId, channels = [], externalMemoryInstructions, ttsSystemHint, extraSystemPrompt, activeProjectContext, silentReplyPromptMode = "generic", promptContribution, includeProblemSolving = true, includeToneSection = true, actionTrustLevel, responseLanguage = "auto", customInstructions } = options;
|
|
20
20
|
if (promptMode === "none") return joinSections([
|
|
21
21
|
"You are a personal AI assistant running inside xopc.",
|
|
22
22
|
customInstructions?.trim() ? `<custom_instructions>\n${customInstructions.trim()}\n</custom_instructions>` : void 0,
|
|
@@ -24,13 +24,10 @@ function buildSystemPrompt(workspaceDir, options = {}) {
|
|
|
24
24
|
activeProjectContext
|
|
25
25
|
]);
|
|
26
26
|
const isMinimal = promptMode === "minimal";
|
|
27
|
-
const toolNames = toolNamesOption ?? availableTools;
|
|
28
27
|
const normalizedTools = new Set(toolNames.map((tool) => tool.toLowerCase()));
|
|
29
28
|
const effectiveAgentId = agentId ?? runtime?.agentId;
|
|
30
29
|
const sectionOverrides = promptContribution?.sectionOverrides ?? {};
|
|
31
|
-
const orderedContextFiles = sortContextFilesForPrompt(contextFiles.filter((file) => file.path.trim().length > 0));
|
|
32
|
-
const stableContextFiles = orderedContextFiles.filter((file) => !isDynamicContextFile(file.path));
|
|
33
|
-
const dynamicContextFiles = orderedContextFiles.filter((file) => isDynamicContextFile(file.path));
|
|
30
|
+
const orderedContextFiles = sortContextFilesForPrompt(contextFiles.filter((file) => file.path.trim().length > 0 && !isHeartbeatContextFile(file.path)));
|
|
34
31
|
const hasProfileMemory = orderedContextFiles.some((file) => getContextFileBasename(file.path) === "memory.md");
|
|
35
32
|
const stableSections = [
|
|
36
33
|
"You are a personal AI assistant running inside xopc.",
|
|
@@ -79,20 +76,14 @@ function buildSystemPrompt(workspaceDir, options = {}) {
|
|
|
79
76
|
}));
|
|
80
77
|
if (!isMinimal && ttsSystemHint?.trim()) stableSections.push(`## Voice (TTS)\n\n${ttsSystemHint.trim()}`);
|
|
81
78
|
stableSections.push(joinSections(buildProjectContextSection({
|
|
82
|
-
files:
|
|
83
|
-
heading: "# Project Context"
|
|
84
|
-
dynamic: false
|
|
79
|
+
files: orderedContextFiles,
|
|
80
|
+
heading: "# Project Context"
|
|
85
81
|
})));
|
|
86
82
|
if (!isMinimal) stableSections.push(buildSilentRepliesSection({
|
|
87
83
|
isMinimal,
|
|
88
84
|
silentReplyPromptMode
|
|
89
85
|
}));
|
|
90
86
|
const dynamicSections = [];
|
|
91
|
-
dynamicSections.push(joinSections(buildProjectContextSection({
|
|
92
|
-
files: dynamicContextFiles,
|
|
93
|
-
heading: stableContextFiles.length > 0 ? "# Dynamic Project Context" : "# Project Context",
|
|
94
|
-
dynamic: true
|
|
95
|
-
})));
|
|
96
87
|
if (extraSystemPrompt?.trim()) {
|
|
97
88
|
const contextHeader = isMinimal ? "## Subagent Context" : "## Group Chat Context";
|
|
98
89
|
dynamicSections.push(`${contextHeader}\n\n${extraSystemPrompt.trim()}`);
|
|
@@ -129,4 +120,4 @@ function splitBuiltSystemPrompt(text) {
|
|
|
129
120
|
return splitPromptCacheBoundary(text);
|
|
130
121
|
}
|
|
131
122
|
//#endregion
|
|
132
|
-
export { buildSystemPrompt, getContextFileBasename,
|
|
123
|
+
export { buildSystemPrompt, getContextFileBasename, sortContextFilesForPrompt, splitBuiltSystemPrompt };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
//#region src/agent/tools/cache-stability.ts
|
|
2
|
+
const TOOL_ORDER = new Map([
|
|
3
|
+
["xopc_tool_search", 10],
|
|
4
|
+
["xopc_tool_describe", 20],
|
|
5
|
+
["xopc_tool_execute", 30]
|
|
6
|
+
]);
|
|
7
|
+
/** Provider-visible tool order is part of the cached prompt prefix. */
|
|
8
|
+
function sortToolsForPromptCache(tools) {
|
|
9
|
+
return [...tools].sort((left, right) => {
|
|
10
|
+
const leftOrder = TOOL_ORDER.get(left.name) ?? Number.MAX_SAFE_INTEGER;
|
|
11
|
+
const rightOrder = TOOL_ORDER.get(right.name) ?? Number.MAX_SAFE_INTEGER;
|
|
12
|
+
if (leftOrder !== rightOrder) return leftOrder - rightOrder;
|
|
13
|
+
const byName = left.name.localeCompare(right.name);
|
|
14
|
+
if (byName !== 0) return byName;
|
|
15
|
+
return left.description.localeCompare(right.description);
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
export { sortToolsForPromptCache };
|
|
@@ -55,6 +55,7 @@ import { mergeTtsConfigFromAppConfig } from "../../voice/tts/merge-config.js";
|
|
|
55
55
|
import { createTextToSpeechTool } from "./tts-tool.js";
|
|
56
56
|
import { getAgentCapabilityToolNames } from "../capabilities/registry.js";
|
|
57
57
|
import "../capabilities/index.js";
|
|
58
|
+
import { sortToolsForPromptCache } from "./cache-stability.js";
|
|
58
59
|
//#region src/agent/tools/factory.ts
|
|
59
60
|
init_agent_scope();
|
|
60
61
|
init_logger();
|
|
@@ -352,7 +353,7 @@ var AgentToolsFactory = class {
|
|
|
352
353
|
return ["create_desktop_pet"];
|
|
353
354
|
}
|
|
354
355
|
createAllTools(coreOptions) {
|
|
355
|
-
return wrapToolsWithProtection(this.createCoreTools(coreOptions), this.deps.toolExecutorConfig);
|
|
356
|
+
return sortToolsForPromptCache(wrapToolsWithProtection(this.createCoreTools(coreOptions), this.deps.toolExecutorConfig));
|
|
356
357
|
}
|
|
357
358
|
};
|
|
358
359
|
function filterToolsByDisabledSet(tools, disabled) {
|
|
@@ -250,11 +250,16 @@ export declare const RuntimePolicySchema: z.ZodOptional<z.ZodObject<{
|
|
|
250
250
|
maxTurns: z.ZodOptional<z.ZodNumber>;
|
|
251
251
|
timeoutMs: z.ZodOptional<z.ZodNumber>;
|
|
252
252
|
maxToolFailuresPerTurn: z.ZodOptional<z.ZodNumber>;
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
253
|
+
promptCache: z.ZodOptional<z.ZodObject<{
|
|
254
|
+
mode: z.ZodDefault<z.ZodEnum<{
|
|
255
|
+
off: "off";
|
|
256
|
+
auto: "auto";
|
|
257
|
+
}>>;
|
|
258
|
+
lifetime: z.ZodDefault<z.ZodEnum<{
|
|
259
|
+
short: "short";
|
|
260
|
+
long: "long";
|
|
261
|
+
}>>;
|
|
262
|
+
}, z.core.$strict>>;
|
|
258
263
|
}, z.core.$strict>>;
|
|
259
264
|
export declare const PromptPolicySchema: z.ZodOptional<z.ZodObject<{
|
|
260
265
|
customInstructions: z.ZodOptional<z.ZodString>;
|
|
@@ -399,11 +404,16 @@ export declare const AgentManifestSchema: z.ZodObject<{
|
|
|
399
404
|
maxTurns: z.ZodOptional<z.ZodNumber>;
|
|
400
405
|
timeoutMs: z.ZodOptional<z.ZodNumber>;
|
|
401
406
|
maxToolFailuresPerTurn: z.ZodOptional<z.ZodNumber>;
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
+
promptCache: z.ZodOptional<z.ZodObject<{
|
|
408
|
+
mode: z.ZodDefault<z.ZodEnum<{
|
|
409
|
+
off: "off";
|
|
410
|
+
auto: "auto";
|
|
411
|
+
}>>;
|
|
412
|
+
lifetime: z.ZodDefault<z.ZodEnum<{
|
|
413
|
+
short: "short";
|
|
414
|
+
long: "long";
|
|
415
|
+
}>>;
|
|
416
|
+
}, z.core.$strict>>;
|
|
407
417
|
}, z.core.$strict>>;
|
|
408
418
|
prompt: z.ZodOptional<z.ZodObject<{
|
|
409
419
|
customInstructions: z.ZodOptional<z.ZodString>;
|
|
@@ -536,11 +546,16 @@ export declare const AgentConfigEntrySchema: z.ZodObject<{
|
|
|
536
546
|
maxTurns: z.ZodOptional<z.ZodNumber>;
|
|
537
547
|
timeoutMs: z.ZodOptional<z.ZodNumber>;
|
|
538
548
|
maxToolFailuresPerTurn: z.ZodOptional<z.ZodNumber>;
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
549
|
+
promptCache: z.ZodOptional<z.ZodObject<{
|
|
550
|
+
mode: z.ZodDefault<z.ZodEnum<{
|
|
551
|
+
off: "off";
|
|
552
|
+
auto: "auto";
|
|
553
|
+
}>>;
|
|
554
|
+
lifetime: z.ZodDefault<z.ZodEnum<{
|
|
555
|
+
short: "short";
|
|
556
|
+
long: "long";
|
|
557
|
+
}>>;
|
|
558
|
+
}, z.core.$strict>>;
|
|
544
559
|
}, z.core.$strict>>;
|
|
545
560
|
prompt: z.ZodOptional<z.ZodObject<{
|
|
546
561
|
customInstructions: z.ZodOptional<z.ZodString>;
|
|
@@ -659,11 +674,16 @@ export declare const CapabilityPresetSchema: z.ZodObject<{
|
|
|
659
674
|
maxTurns: z.ZodOptional<z.ZodNumber>;
|
|
660
675
|
timeoutMs: z.ZodOptional<z.ZodNumber>;
|
|
661
676
|
maxToolFailuresPerTurn: z.ZodOptional<z.ZodNumber>;
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
677
|
+
promptCache: z.ZodOptional<z.ZodObject<{
|
|
678
|
+
mode: z.ZodDefault<z.ZodEnum<{
|
|
679
|
+
off: "off";
|
|
680
|
+
auto: "auto";
|
|
681
|
+
}>>;
|
|
682
|
+
lifetime: z.ZodDefault<z.ZodEnum<{
|
|
683
|
+
short: "short";
|
|
684
|
+
long: "long";
|
|
685
|
+
}>>;
|
|
686
|
+
}, z.core.$strict>>;
|
|
667
687
|
}, z.core.$strict>>;
|
|
668
688
|
locks: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
669
689
|
}, z.core.$strict>;
|
|
@@ -157,11 +157,10 @@ var init_schema = __esmMin((() => {
|
|
|
157
157
|
maxTurns: z.number().int().positive().optional(),
|
|
158
158
|
timeoutMs: z.number().int().positive().optional(),
|
|
159
159
|
maxToolFailuresPerTurn: z.number().int().positive().optional(),
|
|
160
|
-
|
|
161
|
-
"
|
|
162
|
-
"short",
|
|
163
|
-
|
|
164
|
-
]).optional()
|
|
160
|
+
promptCache: z.object({
|
|
161
|
+
mode: z.enum(["off", "auto"]).default("auto"),
|
|
162
|
+
lifetime: z.enum(["short", "long"]).default("short")
|
|
163
|
+
}).strict().optional()
|
|
165
164
|
}).strict().optional();
|
|
166
165
|
PromptPolicySchema = z.object({ customInstructions: z.string().optional() }).strict().optional();
|
|
167
166
|
AgentIdentitySchema = z.object({
|