@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.
Files changed (164) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/telegram/xopc.extension.json +1 -1
  3. package/dist/gateway/static/root/assets/{activity-store-DOhnAQOQ.js → activity-store-SZAay-ze.js} +1 -1
  4. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-D8Cw0Mve.js → agent-avatar-dicebear-DPLJtxoS.js} +1 -1
  5. package/dist/gateway/static/root/assets/agents-DyjRuuRa.js +68 -0
  6. package/dist/gateway/static/root/assets/{api-E9Bk-Idp.js → api-C1cSt2ru.js} +1 -1
  7. package/dist/gateway/static/root/assets/{app-management-settings-panel-BubrReXg.js → app-management-settings-panel-B3PCMqor.js} +1 -1
  8. package/dist/gateway/static/root/assets/{appearance-settings-BY5Az2p4.js → appearance-settings-txSx3REK.js} +1 -1
  9. package/dist/gateway/static/root/assets/apps-page-BEhBaooZ.js +1 -0
  10. package/dist/gateway/static/root/assets/{archive-plugin-BY8843mB.js → archive-plugin-B2VVc17j.js} +1 -1
  11. package/dist/gateway/static/root/assets/{attachment-load-DG45KScf.js → attachment-load-Crk7x8qR.js} +1 -1
  12. package/dist/gateway/static/root/assets/automations-page-BUC6FL6O.js +6 -0
  13. package/dist/gateway/static/root/assets/{binary-plugins-DGIF2ZAv.js → binary-plugins-CLlJsq5D.js} +1 -1
  14. package/dist/gateway/static/root/assets/{block-editor-C1Qktr1g.js → block-editor-cJ5lVNAG.js} +46 -46
  15. package/dist/gateway/static/root/assets/browser-settings-page-Bq2m4VpX.js +8 -0
  16. package/dist/gateway/static/root/assets/{browser-workflow-inputs-D_jnndyz.js → browser-workflow-inputs-WRiFG88u.js} +1 -1
  17. package/dist/gateway/static/root/assets/{browser-workflows-page-DyIRvPaq.js → browser-workflows-page-BHLdVoOC.js} +2 -2
  18. package/dist/gateway/static/root/assets/capabilities-settings-panel-BiQbVvYU.js +7 -0
  19. package/dist/gateway/static/root/assets/{capability-presets-api--tz0mGLY.js → capability-presets-api-DmElfgC9.js} +1 -1
  20. package/dist/gateway/static/root/assets/capability-presets-settings-panel-CYlZQpLY.js +3 -0
  21. package/dist/gateway/static/root/assets/{channel-recipient-api-CkOKF9nc.js → channel-recipient-api-BUEOtPft.js} +1 -1
  22. package/dist/gateway/static/root/assets/channels-settings-DlDiCMhO.js +1 -0
  23. package/dist/gateway/static/root/assets/channels-status-swr-Dm_-934M.js +1 -0
  24. package/dist/gateway/static/root/assets/{connectors-api-FBOe9SWC.js → connectors-api-BCdn44_o.js} +1 -1
  25. package/dist/gateway/static/root/assets/connectors-page-Ce-eaPHw.js +2 -0
  26. package/dist/gateway/static/root/assets/cron-expression-D2hF3t68.js +2 -0
  27. package/dist/gateway/static/root/assets/{date-picker-Dtn7e6TW.js → date-picker-Dvjoq2mB.js} +1 -1
  28. package/dist/gateway/static/root/assets/dependency-picker-zv1OIhk3.js +1 -0
  29. package/dist/gateway/static/root/assets/desktop-pet-DJZhKLTb.js +1 -0
  30. package/dist/gateway/static/root/assets/{desktop-pet-settings-FDmaty3P.js → desktop-pet-settings-B3ubM-eY.js} +1 -1
  31. package/dist/gateway/static/root/assets/{directory-picker-path-field-X2e8xn8j.js → directory-picker-path-field-BZEJIWOT.js} +1 -1
  32. package/dist/gateway/static/root/assets/download-Dk-ZjtO6.js +1 -0
  33. package/dist/gateway/static/root/assets/{extension-debug-page-Dx4MBfga.js → extension-debug-page-Bvq3E1A1.js} +1 -1
  34. package/dist/gateway/static/root/assets/{extension-page-Bf_FEDhM.js → extension-page-CuCJjWNm.js} +1 -1
  35. package/dist/gateway/static/root/assets/{extension-settings-page-CrOgZxkf.js → extension-settings-page-CpTk_ajr.js} +1 -1
  36. package/dist/gateway/static/root/assets/{gateway-config-api-CZ4r-7qT.js → gateway-config-api-C2yM6qj7.js} +1 -1
  37. package/dist/gateway/static/root/assets/{gateway-config-swr-Cv2ywpdb.js → gateway-config-swr-CN-JHMAh.js} +1 -1
  38. package/dist/gateway/static/root/assets/{gateway-settings-DpWmVi0w.js → gateway-settings-COwi--2Q.js} +1 -1
  39. package/dist/gateway/static/root/assets/{gateway-startup-retry-undf-k5g.js → gateway-startup-retry-Dq1RyCjz.js} +1 -1
  40. package/dist/gateway/static/root/assets/{heartbeat-settings-DSQZhSvT.js → heartbeat-settings-Dc6H4VGF.js} +1 -1
  41. package/dist/gateway/static/root/assets/home-page-BB3RGxwm.js +3 -0
  42. package/dist/gateway/static/root/assets/index-CPTLS-Zn.js +85 -0
  43. package/dist/gateway/static/root/assets/index-Dm-cvIpl.css +2 -0
  44. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-Bc3Quw42.js → keyboard-shortcuts-settings-cue30Z5p.js} +1 -1
  45. package/dist/gateway/static/root/assets/local-app-workbench-page-DfO6FOB9.js +2 -0
  46. package/dist/gateway/static/root/assets/local-apps-page-DKQSCsa0.js +1 -0
  47. package/dist/gateway/static/root/assets/{locale-store-CnamlR07.js → locale-store-81Gv0wwo.js} +1 -1
  48. package/dist/gateway/static/root/assets/logs-page-DODbODyl.js +2 -0
  49. package/dist/gateway/static/root/assets/{management-settings-CJmcaVje.js → management-settings--4H26QR-.js} +1 -1
  50. package/dist/gateway/static/root/assets/{markdown-split-AcuFp0TH.js → markdown-split-BTvpyjAi.js} +1 -1
  51. package/dist/gateway/static/root/assets/markdown-view-B12xeflb.css +1 -0
  52. package/dist/gateway/static/root/assets/markdown-view-B4iBrVWK.js +69 -0
  53. package/dist/gateway/static/root/assets/{media-plugins-BvFgsiJL.js → media-plugins-DrejWl8t.js} +1 -1
  54. package/dist/gateway/static/root/assets/messages-ELK_1h2D.js +3 -0
  55. package/dist/gateway/static/root/assets/{notes-page-IW7IQ9nJ.js → notes-page-DfSzeQKd.js} +1 -1
  56. package/dist/gateway/static/root/assets/notes-workbench-DxL4GhwV.js +4 -0
  57. package/dist/gateway/static/root/assets/plus-Q2yW6OE7.js +1 -0
  58. package/dist/gateway/static/root/assets/preview-open-alternatives-yjh1_7K3.js +1 -0
  59. package/dist/gateway/static/root/assets/{product-open-page-CWEzLykF.js → product-open-page-DopWBJLa.js} +1 -1
  60. package/dist/gateway/static/root/assets/project-detail-page-Bl3LJHZB.js +1 -0
  61. package/dist/gateway/static/root/assets/projects-page-DStOErTC.js +1 -0
  62. package/dist/gateway/static/root/assets/remote-access-hub-C_hh9ztK.js +1 -0
  63. package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-DAohEZrX.js → runtime-tools-settings-panel-_dHhbYV2.js} +2 -2
  64. package/dist/gateway/static/root/assets/{schema-form-C_OBW8A5.js → schema-form-DZhORvJZ.js} +1 -1
  65. package/dist/gateway/static/root/assets/sessions-page-DDY21KYw.js +1 -0
  66. package/dist/gateway/static/root/assets/settings-advanced-gate-BJrHIcw8.js +1 -0
  67. package/dist/gateway/static/root/assets/{settings-form-section-C7w2WS-A.js → settings-form-section-Du5eAgFS.js} +1 -1
  68. package/dist/gateway/static/root/assets/{settings-loading-skeleton-zwibve4m.js → settings-loading-skeleton-DWS27sv1.js} +1 -1
  69. package/dist/gateway/static/root/assets/{settings-page-B9W03dSW.js → settings-page-DPD0MwoN.js} +1 -1
  70. package/dist/gateway/static/root/assets/setup-status-panel-CnXN4LH_.js +1 -0
  71. package/dist/gateway/static/root/assets/share-preview-page-D6HmH2ga.js +2 -0
  72. package/dist/gateway/static/root/assets/shares-settings-U3lvtO3Y.js +1 -0
  73. package/dist/gateway/static/root/assets/skill-reload-api-B6GiZgZU.js +1 -0
  74. package/dist/gateway/static/root/assets/skills-page-XVO7d3fh.js +2 -0
  75. package/dist/gateway/static/root/assets/{skills-page.utils-D9QT5cxj.js → skills-page.utils-DwxIpWrI.js} +1 -1
  76. package/dist/gateway/static/root/assets/{system-settings-panel-c1HVtv1Y.js → system-settings-panel-BmKbxR8_.js} +1 -1
  77. package/dist/gateway/static/root/assets/task-detail-page-DA6ItYrc.js +1 -0
  78. package/dist/gateway/static/root/assets/{theme-store-BDyUTObE.js → theme-store-hOF81tUQ.js} +1 -1
  79. package/dist/gateway/static/root/assets/{use-autosave-D6UkH_Ga.js → use-autosave-GJfDpf66.js} +1 -1
  80. package/dist/gateway/static/root/assets/user-context-page-CQ1QrRE6.js +1 -0
  81. package/dist/gateway/static/root/assets/{voice-api-key-field-BOxkKbGh.js → voice-api-key-field-B0zve9EE.js} +1 -1
  82. package/dist/gateway/static/root/assets/work-discovery-overlay-DrIbHnBz.js +1 -0
  83. package/dist/gateway/static/root/assets/work-discovery-page-CQW8pPec.js +1 -0
  84. package/dist/gateway/static/root/assets/{workflow-run-setup-panel-ZA3bP65I.js → workflow-run-setup-panel-D-_zQxim.js} +2 -2
  85. package/dist/gateway/static/root/assets/workflows-page-O21l_f5c.js +5 -0
  86. package/dist/gateway/static/root/assets/{working-directory-picker-modal-BcyiDXU3.js → working-directory-picker-modal-5xYx48tV.js} +1 -1
  87. package/dist/gateway/static/root/assets/x-B_N4OPjy.js +1 -0
  88. package/dist/gateway/static/root/index.html +19 -14
  89. package/dist/package.js +1 -1
  90. package/dist/src/agent/agent-manager.js +2 -1
  91. package/dist/src/agent/embedded/run-for-session.js +3 -2
  92. package/dist/src/agent/embedded/run-turn.js +29 -16
  93. package/dist/src/agent/embedded/types.d.ts +2 -2
  94. package/dist/src/agent/embedded/xopc-stream-bridge.d.ts +2 -1
  95. package/dist/src/agent/embedded/xopc-stream-bridge.js +2 -2
  96. package/dist/src/agent/memory/context-budget.d.ts +6 -1
  97. package/dist/src/agent/memory/context-budget.js +94 -2
  98. package/dist/src/agent/prompt/cache-boundary.d.ts +2 -0
  99. package/dist/src/agent/prompt/cache-boundary.js +10 -1
  100. package/dist/src/agent/prompt/sections/project-context.d.ts +1 -4
  101. package/dist/src/agent/prompt/sections/project-context.js +6 -13
  102. package/dist/src/agent/prompt/service-prompt-builder.js +2 -1
  103. package/dist/src/agent/prompt/system-prompt.d.ts +1 -3
  104. package/dist/src/agent/prompt/system-prompt.js +6 -15
  105. package/dist/src/agent/tools/cache-stability.d.ts +3 -0
  106. package/dist/src/agent/tools/cache-stability.js +19 -0
  107. package/dist/src/agent/tools/factory.js +2 -1
  108. package/dist/src/agent-manifest/schema.d.ts +40 -20
  109. package/dist/src/agent-manifest/schema.js +4 -5
  110. package/dist/src/config/schema.d.ts +40 -20
  111. package/dist/src/extensions/types/providers.d.ts +0 -2
  112. package/dist/src/gateway/hono/lib/config-payload.d.ts +4 -1
  113. package/dist/src/gateway/hono/oauth-async.d.ts +1 -0
  114. package/dist/src/gateway/hono/oauth-async.js +14 -2
  115. package/dist/src/providers/extension-stream-bridge.js +0 -2
  116. package/dist/src/providers/google-managed-prompt-cache.d.ts +14 -0
  117. package/dist/src/providers/google-managed-prompt-cache.js +125 -0
  118. package/dist/src/providers/prompt-cache-fingerprint.d.ts +4 -0
  119. package/dist/src/providers/prompt-cache-fingerprint.js +44 -0
  120. package/dist/src/providers/prompt-cache-lifecycle.d.ts +9 -0
  121. package/dist/src/providers/prompt-cache-lifecycle.js +31 -0
  122. package/dist/src/providers/prompt-cache-observability.d.ts +20 -0
  123. package/dist/src/providers/prompt-cache-observability.js +44 -0
  124. package/dist/src/providers/prompt-cache-payload.d.ts +10 -3
  125. package/dist/src/providers/prompt-cache-payload.js +204 -31
  126. package/dist/src/providers/prompt-cache-plan.d.ts +15 -0
  127. package/dist/src/providers/prompt-cache-plan.js +29 -0
  128. package/package.json +3 -3
  129. package/dist/gateway/static/root/assets/agents-ANavi648.js +0 -68
  130. package/dist/gateway/static/root/assets/apps-page-CFh9MBxc.js +0 -1
  131. package/dist/gateway/static/root/assets/automations-page-751CeNI3.js +0 -6
  132. package/dist/gateway/static/root/assets/browser-settings-page-CGAWs-TT.js +0 -8
  133. package/dist/gateway/static/root/assets/capabilities-settings-panel-CghUK68J.js +0 -7
  134. package/dist/gateway/static/root/assets/capability-presets-settings-panel-XsTfkvUn.js +0 -3
  135. package/dist/gateway/static/root/assets/channels-settings-DkoAZZMe.js +0 -1
  136. package/dist/gateway/static/root/assets/channels-status-swr-DNDvMWpl.js +0 -1
  137. package/dist/gateway/static/root/assets/connectors-page-e4vekkAu.js +0 -2
  138. package/dist/gateway/static/root/assets/cron-expression-Q4QaYjpI.js +0 -2
  139. package/dist/gateway/static/root/assets/dependency-picker-Br6nPaM6.js +0 -1
  140. package/dist/gateway/static/root/assets/desktop-pet-DzTM6aH8.js +0 -1
  141. package/dist/gateway/static/root/assets/home-page-L0uHiu0T.js +0 -3
  142. package/dist/gateway/static/root/assets/index-Bp8I_eX4.css +0 -1
  143. package/dist/gateway/static/root/assets/index-DRrjYYzZ.js +0 -152
  144. package/dist/gateway/static/root/assets/local-app-workbench-page-Copi3sdP.js +0 -2
  145. package/dist/gateway/static/root/assets/local-apps-page-CP9X_vtz.js +0 -1
  146. package/dist/gateway/static/root/assets/logs-page-DMKq6e29.js +0 -2
  147. package/dist/gateway/static/root/assets/messages-D7kOD_o_.js +0 -3
  148. package/dist/gateway/static/root/assets/notes-workbench-Zb8U0anO.js +0 -4
  149. package/dist/gateway/static/root/assets/preview-open-alternatives-C-b34dgQ.js +0 -1
  150. package/dist/gateway/static/root/assets/project-detail-page-BuIbLnan.js +0 -1
  151. package/dist/gateway/static/root/assets/projects-page-BwUfRqNV.js +0 -1
  152. package/dist/gateway/static/root/assets/remote-access-hub-BTaXxcEi.js +0 -1
  153. package/dist/gateway/static/root/assets/sessions-page-BnHoTCD3.js +0 -1
  154. package/dist/gateway/static/root/assets/settings-advanced-gate-BTB_fxjt.js +0 -1
  155. package/dist/gateway/static/root/assets/setup-status-panel-C8Hec2Af.js +0 -1
  156. package/dist/gateway/static/root/assets/share-preview-page-6e3ks2be.js +0 -2
  157. package/dist/gateway/static/root/assets/shares-settings-Bwc5HfLI.js +0 -1
  158. package/dist/gateway/static/root/assets/skill-reload-api-D7kunhqy.js +0 -1
  159. package/dist/gateway/static/root/assets/skills-page-WgkaNzpc.js +0 -2
  160. package/dist/gateway/static/root/assets/task-detail-page-CjP-6WVU.js +0 -1
  161. package/dist/gateway/static/root/assets/user-context-page-D8ZhPDNF.js +0 -1
  162. package/dist/gateway/static/root/assets/work-discovery-overlay-Dd19yK9t.js +0 -1
  163. package/dist/gateway/static/root/assets/work-discovery-page-DUkdJl1e.js +0 -1
  164. package/dist/gateway/static/root/assets/workflows-page-C2b5W5bw.js +0 -5
@@ -1,15 +1,25 @@
1
1
  import { splitPromptCacheBoundary, stripPromptCacheBoundary } from "../agent/prompt/cache-boundary.js";
2
- import { createHash } from "node:crypto";
2
+ import { canonicalizeCacheValue, digestCacheText } from "./prompt-cache-fingerprint.js";
3
+ import { resolvePromptCachePolicy, resolvePromptCacheProviderMode } from "./prompt-cache-plan.js";
4
+ import { applyGoogleManagedPromptCache } from "./google-managed-prompt-cache.js";
3
5
  //#region src/providers/prompt-cache-payload.ts
4
6
  function asRecord(value) {
5
7
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
6
8
  }
7
- function supportsPromptCacheKey(model) {
8
- if (model.api === "openai-codex-responses" || model.api === "azure-openai-responses") return true;
9
- if (model.api !== "openai-completions" && model.api !== "openai-responses") return false;
10
- return model.provider === "openai" || model.baseUrl?.includes("api.openai.com") === true;
9
+ function buildPromptCacheKey(model, context, reasoning) {
10
+ const stablePrompt = context.systemPrompt ? splitPromptCacheBoundary(context.systemPrompt)?.stablePrefix ?? stripPromptCacheBoundary(context.systemPrompt) : "";
11
+ return `xopc-v2-${digestCacheText(`v2\0${model.provider}\0${model.id}\0${reasoning ?? ""}\0${stablePrompt}\0${canonicalizeCacheValue(context.tools ?? [])}`)}`;
11
12
  }
12
- function splitAnthropicSystem(payload) {
13
+ function buildPromptCachePlan(model, context, policyInput, reasoning) {
14
+ const policy = resolvePromptCachePolicy(policyInput);
15
+ const providerMode = resolvePromptCacheProviderMode(model, policy);
16
+ return {
17
+ policy,
18
+ providerMode,
19
+ ...providerMode === "implicit" || providerMode === "explicit" ? { cacheKey: buildPromptCacheKey(model, context, reasoning) } : {}
20
+ };
21
+ }
22
+ function splitAnthropicSystem(payload, lifetime) {
13
23
  const record = asRecord(payload);
14
24
  if (!record || !Array.isArray(record.system)) return payload;
15
25
  let changed = false;
@@ -23,7 +33,11 @@ function splitAnthropicSystem(payload) {
23
33
  return [{
24
34
  ...rest,
25
35
  type: block.type ?? "text",
26
- text: split.stablePrefix
36
+ text: split.stablePrefix,
37
+ cache_control: lifetime === "long" ? {
38
+ type: "ephemeral",
39
+ ttl: "1h"
40
+ } : { type: "ephemeral" }
27
41
  }, ...split.dynamicSuffix ? [{
28
42
  type: block.type ?? "text",
29
43
  text: split.dynamicSuffix
@@ -34,6 +48,54 @@ function splitAnthropicSystem(payload) {
34
48
  system
35
49
  } : payload;
36
50
  }
51
+ function addAnthropicHistoryBreakpoint(payload, lifetime) {
52
+ const record = asRecord(payload);
53
+ if (!record || !Array.isArray(record.messages)) return payload;
54
+ const messages = [...record.messages];
55
+ const cacheControl = lifetime === "long" ? {
56
+ type: "ephemeral",
57
+ ttl: "1h"
58
+ } : { type: "ephemeral" };
59
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
60
+ const message = asRecord(messages[index]);
61
+ if (!message || message.role !== "user") continue;
62
+ const blocks = Array.isArray(message.content) ? [...message.content] : void 0;
63
+ const containsToolResult = blocks?.some((block) => asRecord(block)?.type === "tool_result") ?? false;
64
+ if (index === messages.length - 1 && !containsToolResult) continue;
65
+ if (!blocks) {
66
+ if (typeof message.content !== "string") continue;
67
+ messages[index] = {
68
+ ...message,
69
+ content: [{
70
+ type: "text",
71
+ text: message.content,
72
+ cache_control: cacheControl
73
+ }]
74
+ };
75
+ return {
76
+ ...record,
77
+ messages
78
+ };
79
+ }
80
+ for (let blockIndex = blocks.length - 1; blockIndex >= 0; blockIndex -= 1) {
81
+ const block = asRecord(blocks[blockIndex]);
82
+ if (!block) continue;
83
+ blocks[blockIndex] = {
84
+ ...block,
85
+ cache_control: cacheControl
86
+ };
87
+ messages[index] = {
88
+ ...message,
89
+ content: blocks
90
+ };
91
+ return {
92
+ ...record,
93
+ messages
94
+ };
95
+ }
96
+ }
97
+ return payload;
98
+ }
37
99
  function splitBedrockSystem(payload) {
38
100
  const record = asRecord(payload);
39
101
  if (!record || !Array.isArray(record.system)) return payload;
@@ -56,7 +118,7 @@ function splitBedrockSystem(payload) {
56
118
  if (next?.cachePoint) {
57
119
  system.push(next);
58
120
  index += 1;
59
- }
121
+ } else system.push({ cachePoint: { type: "default" } });
60
122
  if (split.dynamicSuffix) system.push({ text: split.dynamicSuffix });
61
123
  }
62
124
  return changed ? {
@@ -64,20 +126,128 @@ function splitBedrockSystem(payload) {
64
126
  system
65
127
  } : payload;
66
128
  }
67
- function transformPromptCachePayload(model, payload) {
68
- if (model.api === "anthropic-messages") return splitAnthropicSystem(payload);
69
- if (model.api === "bedrock-converse-stream") return splitBedrockSystem(payload);
129
+ function addBedrockHistoryBreakpoint(payload) {
130
+ const record = asRecord(payload);
131
+ if (!record || !Array.isArray(record.messages)) return payload;
132
+ const messages = [...record.messages];
133
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
134
+ const message = asRecord(messages[index]);
135
+ if (!message || message.role !== "user" || !Array.isArray(message.content)) continue;
136
+ const content = [...message.content];
137
+ const containsToolResult = content.some((block) => "toolResult" in (asRecord(block) ?? {}));
138
+ if (index === messages.length - 1 && !containsToolResult) continue;
139
+ content.push({ cachePoint: { type: "default" } });
140
+ messages[index] = {
141
+ ...message,
142
+ content
143
+ };
144
+ return {
145
+ ...record,
146
+ messages
147
+ };
148
+ }
70
149
  return payload;
71
150
  }
72
- function canonicalJson(value) {
73
- if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
74
- const record = asRecord(value);
75
- if (record) return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`).join(",")}}`;
76
- return JSON.stringify(value) ?? "null";
151
+ function addBreakpointToContent(content) {
152
+ if (typeof content === "string") return [{
153
+ type: "input_text",
154
+ text: content,
155
+ prompt_cache_breakpoint: { mode: "explicit" }
156
+ }];
157
+ if (!Array.isArray(content)) return content;
158
+ const blocks = [...content];
159
+ for (let index = blocks.length - 1; index >= 0; index -= 1) {
160
+ const block = asRecord(blocks[index]);
161
+ if (!block) continue;
162
+ blocks[index] = {
163
+ ...block,
164
+ prompt_cache_breakpoint: { mode: "explicit" }
165
+ };
166
+ return blocks;
167
+ }
168
+ return content;
77
169
  }
78
- function buildPromptCacheKey(model, context) {
79
- const stablePrompt = context.systemPrompt ? splitPromptCacheBoundary(context.systemPrompt)?.stablePrefix ?? stripPromptCacheBoundary(context.systemPrompt) : "";
80
- return `xopc-${createHash("sha256").update(`${model.provider}\0${model.id}\0${stablePrompt}\0${canonicalJson(context.tools ?? [])}`).digest("base64url")}`;
170
+ function addOpenAIHistoryBreakpoint(items) {
171
+ if (!Array.isArray(items)) return items;
172
+ const next = [...items];
173
+ for (let index = next.length - 1; index >= 0; index -= 1) {
174
+ const item = asRecord(next[index]);
175
+ if (!item) continue;
176
+ if (item.type === "function_call_output") {
177
+ next[index] = {
178
+ ...item,
179
+ output: addBreakpointToContent(item.output)
180
+ };
181
+ return next;
182
+ }
183
+ if (!("content" in item)) continue;
184
+ if (index === next.length - 1 && item.role === "user") continue;
185
+ const content = addBreakpointToContent(item.content);
186
+ if (content === item.content) continue;
187
+ next[index] = {
188
+ ...item,
189
+ content
190
+ };
191
+ return next;
192
+ }
193
+ return items;
194
+ }
195
+ function applyOpenAIExplicitCache(payload, context, cacheKey) {
196
+ const record = asRecord(payload);
197
+ if (!record) return payload;
198
+ const split = splitPromptCacheBoundary(context.systemPrompt ?? "");
199
+ const stablePrompt = split?.stablePrefix ?? stripPromptCacheBoundary(context.systemPrompt ?? "");
200
+ const dynamicPrompt = split?.dynamicSuffix ?? "";
201
+ const stableContent = [{
202
+ type: "input_text",
203
+ text: stablePrompt,
204
+ prompt_cache_breakpoint: { mode: "explicit" }
205
+ }];
206
+ const dynamicContent = [{
207
+ type: "input_text",
208
+ text: dynamicPrompt
209
+ }];
210
+ if (Array.isArray(record.input)) {
211
+ const expectedInstructions = stripPromptCacheBoundary(context.systemPrompt ?? "");
212
+ const canReplaceInstructions = record.instructions === void 0 || record.instructions === expectedInstructions;
213
+ return {
214
+ ...record,
215
+ ...canReplaceInstructions ? { instructions: [{
216
+ type: "message",
217
+ role: "system",
218
+ content: stableContent
219
+ }, ...dynamicPrompt ? [{
220
+ type: "message",
221
+ role: "system",
222
+ content: dynamicContent
223
+ }] : []] } : {},
224
+ input: addOpenAIHistoryBreakpoint(record.input),
225
+ prompt_cache_key: cacheKey,
226
+ prompt_cache_options: {
227
+ mode: "explicit",
228
+ ttl: "30m"
229
+ }
230
+ };
231
+ }
232
+ return {
233
+ ...record,
234
+ prompt_cache_key: cacheKey,
235
+ prompt_cache_options: {
236
+ mode: "explicit",
237
+ ttl: "30m"
238
+ }
239
+ };
240
+ }
241
+ function transformPromptCachePayload(model, payload, plan = buildPromptCachePlan(model, {}), context = {}) {
242
+ if (plan.providerMode === "none" || plan.providerMode === "managed") return payload;
243
+ if (model.api === "anthropic-messages") return addAnthropicHistoryBreakpoint(splitAnthropicSystem(payload, plan.policy.lifetime), plan.policy.lifetime);
244
+ if (model.api === "bedrock-converse-stream") return addBedrockHistoryBreakpoint(splitBedrockSystem(payload));
245
+ if (plan.providerMode === "explicit" && plan.cacheKey) return applyOpenAIExplicitCache(payload, context, plan.cacheKey);
246
+ const record = asRecord(payload);
247
+ return record && plan.cacheKey ? {
248
+ ...record,
249
+ prompt_cache_key: plan.cacheKey
250
+ } : payload;
81
251
  }
82
252
  function preparePromptCacheContext(model, context) {
83
253
  if (model.api === "anthropic-messages" || model.api === "bedrock-converse-stream") return context;
@@ -87,24 +257,27 @@ function preparePromptCacheContext(model, context) {
87
257
  systemPrompt: stripPromptCacheBoundary(context.systemPrompt)
88
258
  };
89
259
  }
90
- function withPromptCachePayloadTransform(model, context, options) {
91
- const explicitBoundary = model.api === "anthropic-messages" || model.api === "bedrock-converse-stream";
92
- const openAiCacheKey = supportsPromptCacheKey(model) && options?.cacheRetention !== "none";
93
- if (!explicitBoundary && !openAiCacheKey) return options;
260
+ function withPromptCachePayloadTransform(model, context, options, policyInput) {
261
+ const plan = buildPromptCachePlan(model, context, policyInput, options?.reasoning);
94
262
  const upstream = options?.onPayload;
95
- const promptCacheKey = openAiCacheKey ? buildPromptCacheKey(model, context) : void 0;
263
+ const cacheRetention = model.api === "anthropic-messages" || model.api === "bedrock-converse-stream" ? plan.providerMode === "none" ? "none" : plan.policy.lifetime : "none";
96
264
  return {
97
265
  ...options,
266
+ cacheRetention,
98
267
  onPayload: async (payload, payloadModel) => {
99
268
  const upstreamPayload = await upstream?.(payload, payloadModel);
100
- const transformed = transformPromptCachePayload(model, upstreamPayload === void 0 ? payload : upstreamPayload);
101
- const record = asRecord(transformed);
102
- return record && promptCacheKey ? {
103
- ...record,
104
- prompt_cache_key: promptCacheKey
105
- } : transformed;
269
+ const transformed = transformPromptCachePayload(model, upstreamPayload === void 0 ? payload : upstreamPayload, plan, context);
270
+ if (plan.providerMode !== "managed") return transformed;
271
+ return applyGoogleManagedPromptCache({
272
+ model,
273
+ context,
274
+ payload: transformed,
275
+ policy: plan.policy,
276
+ apiKey: options?.apiKey,
277
+ fetchImpl: options?.fetch
278
+ });
106
279
  }
107
280
  };
108
281
  }
109
282
  //#endregion
110
- export { buildPromptCacheKey, preparePromptCacheContext, transformPromptCachePayload, withPromptCachePayloadTransform };
283
+ export { buildPromptCacheKey, buildPromptCachePlan, preparePromptCacheContext, transformPromptCachePayload, withPromptCachePayloadTransform };
@@ -0,0 +1,15 @@
1
+ import type { Api, Model } from '@earendil-works/pi-ai';
2
+ export interface PromptCachePolicy {
3
+ mode: 'off' | 'auto';
4
+ lifetime: 'short' | 'long';
5
+ }
6
+ export type PromptCacheProviderMode = 'none' | 'implicit' | 'explicit' | 'managed';
7
+ export interface PromptCachePlan {
8
+ policy: PromptCachePolicy;
9
+ providerMode: PromptCacheProviderMode;
10
+ cacheKey?: string;
11
+ }
12
+ export declare const DEFAULT_PROMPT_CACHE_POLICY: PromptCachePolicy;
13
+ export declare function resolvePromptCachePolicy(policy?: Partial<PromptCachePolicy>): PromptCachePolicy;
14
+ export declare function isOfficialOpenAIModel(model: Model<Api>): boolean;
15
+ export declare function resolvePromptCacheProviderMode(model: Model<Api>, policy: PromptCachePolicy): PromptCacheProviderMode;
@@ -0,0 +1,29 @@
1
+ //#region src/providers/prompt-cache-plan.ts
2
+ const DEFAULT_PROMPT_CACHE_POLICY = {
3
+ mode: "auto",
4
+ lifetime: "short"
5
+ };
6
+ function resolvePromptCachePolicy(policy) {
7
+ return {
8
+ mode: policy?.mode ?? DEFAULT_PROMPT_CACHE_POLICY.mode,
9
+ lifetime: policy?.lifetime ?? DEFAULT_PROMPT_CACHE_POLICY.lifetime
10
+ };
11
+ }
12
+ function isOfficialOpenAIModel(model) {
13
+ if (model.api === "openai-codex-responses" || model.api === "azure-openai-responses") return true;
14
+ if (model.api !== "openai-completions" && model.api !== "openai-responses") return false;
15
+ return model.provider === "openai" || model.baseUrl?.includes("api.openai.com") === true;
16
+ }
17
+ function supportsExplicitOpenAICache(model) {
18
+ return model.api === "openai-responses" && isOfficialOpenAIModel(model) && /^gpt-5\.6(?:-|$)/.test(model.id);
19
+ }
20
+ function resolvePromptCacheProviderMode(model, policy) {
21
+ if (policy.mode === "off") return "none";
22
+ if (supportsExplicitOpenAICache(model)) return "explicit";
23
+ if (isOfficialOpenAIModel(model)) return "implicit";
24
+ if (model.api === "anthropic-messages" || model.api === "bedrock-converse-stream") return "explicit";
25
+ if (model.api === "google-generative-ai") return "managed";
26
+ return "none";
27
+ }
28
+ //#endregion
29
+ export { DEFAULT_PROMPT_CACHE_POLICY, isOfficialOpenAIModel, resolvePromptCachePolicy, resolvePromptCacheProviderMode };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xopcai/xopc",
3
- "version": "0.0.201",
3
+ "version": "0.0.203",
4
4
  "description": "Local-first AI system that remembers context, coordinates AI, and sustains long-term progress.",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",
@@ -164,8 +164,8 @@
164
164
  "vue": "^3.5.35",
165
165
  "@xopcai/gateway-contract": "0.0.0",
166
166
  "@xopcai/endpoint-tools-protocol": "0.0.0",
167
- "@xopcai/realtime-protocol": "0.0.0",
168
- "@xopcai/realtime-client": "0.0.0"
167
+ "@xopcai/realtime-client": "0.0.0",
168
+ "@xopcai/realtime-protocol": "0.0.0"
169
169
  },
170
170
  "scripts": {
171
171
  "assets:brand": "node scripts/generate-brand-assets.mjs",