aws-runtime-bridge 1.9.172 → 1.9.175

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 (65) hide show
  1. package/CHANGELOG.md +126 -0
  2. package/dist/adapter/AcodeSdkAdapter.d.ts +5 -1
  3. package/dist/adapter/AcodeSdkAdapter.js +3 -3
  4. package/dist/adapter/types.d.ts +2 -0
  5. package/dist/index.js +1 -1
  6. package/dist/routes/bridge-temp-files.d.ts +1 -0
  7. package/dist/routes/bridge-temp-files.js +2 -0
  8. package/dist/routes/dashboard.js +35 -5
  9. package/dist/routes/file-browser.d.ts +4 -10
  10. package/dist/routes/file-browser.js +1 -1
  11. package/dist/routes/git.d.ts +42 -0
  12. package/dist/routes/git.js +34 -34
  13. package/dist/routes/sessions.js +1 -1
  14. package/dist/routes/tech-stack.d.ts +2 -0
  15. package/dist/routes/tech-stack.js +2 -0
  16. package/dist/routes/terminal.js +19 -19
  17. package/dist/services/agent-pty-manager.d.ts +2 -0
  18. package/dist/services/agent-pty-manager.js +5 -4
  19. package/dist/services/agent-startup-memory.js +4 -4
  20. package/dist/services/bridge-temp-files.d.ts +36 -0
  21. package/dist/services/bridge-temp-files.js +2 -0
  22. package/dist/services/fileSnapshotStore.d.ts +9 -0
  23. package/dist/services/fileSnapshotStore.js +1 -1
  24. package/dist/services/gitignore.d.ts +12 -0
  25. package/dist/services/gitignore.js +2 -0
  26. package/dist/services/log-retention.js +1 -1
  27. package/dist/services/open-doc-events.d.ts +21 -0
  28. package/dist/services/open-doc-events.js +2 -0
  29. package/dist/services/open-doc-registry.d.ts +24 -0
  30. package/dist/services/open-doc-registry.js +2 -0
  31. package/dist/services/request-invalid-dump.js +1 -1
  32. package/dist/services/runtime-resources-snapshot.js +2 -2
  33. package/dist/services/snapshot-retention.d.ts +7 -0
  34. package/dist/services/snapshot-retention.js +2 -0
  35. package/dist/services/tech-stack-scanner.d.ts +31 -0
  36. package/dist/services/tech-stack-scanner.js +2 -0
  37. package/dist/services/tunnel-client.d.ts +42 -0
  38. package/dist/services/tunnel-client.js +1 -1
  39. package/dist/services/workspaceTaskChangeWatcher.js +2 -2
  40. package/package/acode/dist/__tests__/summary-request-helper.d.ts +2 -0
  41. package/package/acode/dist/__tests__/summary-request-helper.js +2 -0
  42. package/package/acode/dist/cache-control.d.ts +26 -0
  43. package/package/acode/dist/cache-control.js +2 -0
  44. package/package/acode/dist/compaction.d.ts +35 -5
  45. package/package/acode/dist/compaction.js +19 -17
  46. package/package/acode/dist/microcompact.d.ts +57 -0
  47. package/package/acode/dist/microcompact.js +2 -0
  48. package/package/acode/dist/runtime.d.ts +4 -0
  49. package/package/acode/dist/runtime.js +40 -33
  50. package/package/acode/dist/token-estimator.d.ts +15 -0
  51. package/package/acode/dist/token-estimator.js +2 -0
  52. package/package/acode/dist/types.d.ts +13 -0
  53. package/package/aws-client-agent-mcp/dist/ability-market-tools.js +1 -1
  54. package/package/aws-client-agent-mcp/dist/agent-client.d.ts +15 -2
  55. package/package/aws-client-agent-mcp/dist/agent-client.js +1 -1
  56. package/package/aws-client-agent-mcp/dist/mcp-server.d.ts +5 -0
  57. package/package/aws-client-agent-mcp/dist/mcp-server.js +25 -23
  58. package/package/aws-client-agent-mcp/dist/mcp-tools.js +7 -7
  59. package/package/aws-client-agent-mcp/dist/memory-store.d.ts +21 -2
  60. package/package/aws-client-agent-mcp/dist/memory-store.js +27 -2
  61. package/package/aws-client-agent-mcp/dist/memory-tools.js +13 -9
  62. package/package/aws-client-agent-mcp/dist/server-memory-store.d.ts +1 -1
  63. package/package/aws-client-agent-mcp/dist/server-memory-store.js +1 -1
  64. package/package/aws-client-agent-mcp/dist/types.d.ts +51 -0
  65. package/package.json +1 -1
@@ -1,2 +1,2 @@
1
- import c from"node:fs/promises";import o from"node:path";import{getAutoRegisterConfigFilePath as N,getBridgeDataHome as I,getRuntimeHomeDir as S}from"../config.js";import{createLogger as x}from"../utils/logger.js";const w=x("log-retention"),m=7,b=7,_=6*60*60*1e3,M="Instance-ai-configuration-debug.log";let r=m,l=null,p=!1,y=!1,h=!1;function A(t,e){h||(h=!0,w.warn(`${t}${e instanceof Error?`: ${e.message}`:""}`))}function E(t){if(t==null||t===""||typeof t=="boolean")return m;const e=Number(t);return!Number.isFinite(e)||e<=0?m:Math.min(Math.floor(e),b)}function Y(t){const e=E(t);return y=!0,e!==r&&(r=e,F()),r}function V(){return r}function k(){r=m}function J(){r=m,y=!1,l&&(clearInterval(l),l=null),p=!1,h=!1}function O(){const t=o.join(I(),"log");return{logDir:t,agentLogDir:o.join(t,"agent"),instanceAiDebugLogPath:o.join(j(),M)}}function j(){return String(process.env.AWS_TEST_HOME||"").trim()||String(process.env.AWS_RUNTIME_HOME_DIR||"").trim()||S()}async function D(t,e,i){try{const n=await c.stat(t);return n.mtimeMs>=e?!1:(await c.unlink(t),i.removedFiles+=1,i.bytesFreed+=n.size,!0)}catch(n){return n.code!=="ENOENT"&&(i.failures+=1,A("[log-retention] \u5220\u9664\u8FC7\u671F\u65E5\u5FD7\u5931\u8D25",n)),!1}}async function $(t,e,i){let n=o.normalize(t);const s=o.normalize(e);for(;n.startsWith(s)&&n!==s;){try{await c.rmdir(n),i.removedDirs+=1}catch{return}n=o.dirname(n)}}async function W(t,e,i){let n;try{n=await c.readdir(t)}catch{return}for(const s of n)!s.startsWith("lifecycle-")||!s.endsWith(".jsonl")||await D(o.join(t,s),e,i)}async function z(t,e,i){const n=o.join(t,"aws-mcp-node");let s;try{s=(await c.readdir(n,{withFileTypes:!0})).filter(u=>u.isDirectory()).map(u=>o.join(n,u.name))}catch{return}for(const g of s){let u;try{u=(await c.readdir(g,{withFileTypes:!0})).filter(a=>a.isDirectory()).map(a=>o.join(g,a.name))}catch{continue}for(const f of u){let a;try{a=(await c.readdir(f,{withFileTypes:!0})).filter(d=>d.isFile()).map(d=>o.join(f,d.name))}catch{continue}let R=!0;for(const T of a)await D(T,e,i)||(R=!1);R&&a.length>0&&await $(f,n,i)}let L;try{L=(await c.readdir(g,{withFileTypes:!0})).filter(a=>a.isFile()).map(a=>o.join(g,a.name))}catch{continue}for(const f of L)await D(f,e,i)}}async function G(t,e,i){await D(t,e,i)}function F(){const t=async()=>{const e={retentionDays:r,removedFiles:0,removedDirs:0,failures:0,bytesFreed:0},i=Date.now()-r*24*60*60*1e3,n=O();try{await W(n.logDir,i,e),await z(n.agentLogDir,i,e),await G(n.instanceAiDebugLogPath,i,e)}catch(s){A("[log-retention] \u6E05\u7406\u8FC7\u671F\u65E5\u5FD7\u51FA\u9519",s)}return(e.removedFiles>0||e.removedDirs>0)&&w.info(`[log-retention] \u6E05\u7406\u5B8C\u6210\uFF1A\u5220\u9664 ${e.removedFiles} \u4E2A\u6587\u4EF6 / ${e.removedDirs} \u4E2A\u76EE\u5F55\uFF0C\u91CA\u653E ${(e.bytesFreed/1024/1024).toFixed(1)} MB\uFF08\u4FDD\u7559 ${r} \u5929\uFF09`),e};return p||(p=!0,v=t().finally(()=>{p=!1})),v}let v=Promise.resolve({retentionDays:r,removedFiles:0,removedDirs:0,failures:0,bytesFreed:0});function X(){if(l)return;const t=String(process.env.AWS_BRIDGE_LOG_RETENTION_DAYS||"").trim();t&&(r=E(t),y=!0),l=setInterval(()=>{F()},_),H().finally(()=>{F(),w.info(`[log-retention] \u65E5\u5FD7\u4FDD\u7559\u7B56\u7565\u5DF2\u542F\u52A8\uFF1A\u4FDD\u7559 ${r} \u5929\uFF0C\u6BCF ${_/36e5}h \u6E05\u7406\u4E00\u6B21`)})}async function H(){try{const t=await c.readFile(N(),"utf-8"),e=JSON.parse(t);e.logRetentionDays!==void 0&&!y&&(r=E(e.logRetentionDays))}catch{}}function q(){l&&(clearInterval(l),l=null)}export{m as DEFAULT_LOG_RETENTION_DAYS,V as getLogRetentionDays,O as getLogRetentionTargets,E as normalizeLogRetentionDays,k as resetLogRetentionDaysForTest,Y as setLogRetentionDays,X as startLogRetention,q as stopLogRetention,J as stopLogRetentionForTest,F as sweepExpiredLogs};
1
+ import c from"node:fs/promises";import o from"node:path";import{getAutoRegisterConfigFilePath as N,getBridgeDataHome as S,getRuntimeHomeDir as I}from"../config.js";import{createLogger as x}from"../utils/logger.js";import{sweepExpiredSnapshots as b}from"./fileSnapshotStore.js";import{getSnapshotRetentionDays as M,setSnapshotRetentionDays as O}from"./snapshot-retention.js";const w=x("log-retention"),m=7,j=7,_=6*60*60*1e3,$="Instance-ai-configuration-debug.log";let r=m,l=null,d=!1,y=!1,h=!1;function A(t,e){h||(h=!0,w.warn(`${t}${e instanceof Error?`: ${e.message}`:""}`))}function E(t){if(t==null||t===""||typeof t=="boolean")return m;const e=Number(t);return!Number.isFinite(e)||e<=0?m:Math.min(Math.floor(e),j)}function q(t){const e=E(t);return y=!0,e!==r&&(r=e,F()),r}function K(){return r}function Q(){r=m}function Z(){r=m,y=!1,l&&(clearInterval(l),l=null),d=!1,h=!1}function W(){const t=o.join(S(),"log");return{logDir:t,agentLogDir:o.join(t,"agent"),instanceAiDebugLogPath:o.join(z(),$)}}function z(){return String(process.env.AWS_TEST_HOME||"").trim()||String(process.env.AWS_RUNTIME_HOME_DIR||"").trim()||I()}async function D(t,e,i){try{const n=await c.stat(t);return n.mtimeMs>=e?!1:(await c.unlink(t),i.removedFiles+=1,i.bytesFreed+=n.size,!0)}catch(n){return n.code!=="ENOENT"&&(i.failures+=1,A("[log-retention] \u5220\u9664\u8FC7\u671F\u65E5\u5FD7\u5931\u8D25",n)),!1}}async function G(t,e,i){let n=o.normalize(t);const s=o.normalize(e);for(;n.startsWith(s)&&n!==s;){try{await c.rmdir(n),i.removedDirs+=1}catch{return}n=o.dirname(n)}}async function H(t,e,i){let n;try{n=await c.readdir(t)}catch{return}for(const s of n)!s.startsWith("lifecycle-")||!s.endsWith(".jsonl")||await D(o.join(t,s),e,i)}async function B(t,e,i){const n=o.join(t,"aws-mcp-node");let s;try{s=(await c.readdir(n,{withFileTypes:!0})).filter(u=>u.isDirectory()).map(u=>o.join(n,u.name))}catch{return}for(const g of s){let u;try{u=(await c.readdir(g,{withFileTypes:!0})).filter(a=>a.isDirectory()).map(a=>o.join(g,a.name))}catch{continue}for(const f of u){let a;try{a=(await c.readdir(f,{withFileTypes:!0})).filter(p=>p.isFile()).map(p=>o.join(f,p.name))}catch{continue}let L=!0;for(const T of a)await D(T,e,i)||(L=!1);L&&a.length>0&&await G(f,n,i)}let v;try{v=(await c.readdir(g,{withFileTypes:!0})).filter(a=>a.isFile()).map(a=>o.join(g,a.name))}catch{continue}for(const f of v)await D(f,e,i)}}async function P(t,e,i){await D(t,e,i)}function F(){const t=async()=>{const e={retentionDays:r,removedFiles:0,removedDirs:0,failures:0,bytesFreed:0},i=Date.now()-r*24*60*60*1e3,n=W();try{await H(n.logDir,i,e),await B(n.agentLogDir,i,e),await P(n.instanceAiDebugLogPath,i,e)}catch(s){A("[log-retention] \u6E05\u7406\u8FC7\u671F\u65E5\u5FD7\u51FA\u9519",s)}return await b(M()),(e.removedFiles>0||e.removedDirs>0)&&w.info(`[log-retention] \u6E05\u7406\u5B8C\u6210\uFF1A\u5220\u9664 ${e.removedFiles} \u4E2A\u6587\u4EF6 / ${e.removedDirs} \u4E2A\u76EE\u5F55\uFF0C\u91CA\u653E ${(e.bytesFreed/1024/1024).toFixed(1)} MB\uFF08\u4FDD\u7559 ${r} \u5929\uFF09`),e};return d||(d=!0,R=t().finally(()=>{d=!1})),R}let R=Promise.resolve({retentionDays:r,removedFiles:0,removedDirs:0,failures:0,bytesFreed:0});function ee(){if(l)return;const t=String(process.env.AWS_BRIDGE_LOG_RETENTION_DAYS||"").trim();t&&(r=E(t),y=!0),l=setInterval(()=>{F()},_),U().finally(()=>{F(),w.info(`[log-retention] \u65E5\u5FD7\u4FDD\u7559\u7B56\u7565\u5DF2\u542F\u52A8\uFF1A\u4FDD\u7559 ${r} \u5929\uFF0C\u6BCF ${_/36e5}h \u6E05\u7406\u4E00\u6B21`)})}async function U(){try{const t=await c.readFile(N(),"utf-8"),e=JSON.parse(t);e.logRetentionDays!==void 0&&!y&&(r=E(e.logRetentionDays)),e.snapshotRetentionDays!==void 0&&O(e.snapshotRetentionDays,!0)}catch{}}function te(){l&&(clearInterval(l),l=null)}export{m as DEFAULT_LOG_RETENTION_DAYS,K as getLogRetentionDays,W as getLogRetentionTargets,E as normalizeLogRetentionDays,Q as resetLogRetentionDaysForTest,q as setLogRetentionDays,ee as startLogRetention,te as stopLogRetention,Z as stopLogRetentionForTest,F as sweepExpiredLogs};
2
2
 
@@ -0,0 +1,21 @@
1
+
2
+ import type { OpenDocKind } from "./open-doc-registry.js";
3
+
4
+ export type OpenDocEvent = {
5
+ action: "open";
6
+ kind: OpenDocKind;
7
+ docId: string;
8
+ name: string;
9
+ detail?: string;
10
+ } | {
11
+ action: "close";
12
+ kind: OpenDocKind;
13
+ } | {
14
+ action: "delete";
15
+ kind: OpenDocKind;
16
+ docId: string;
17
+ };
18
+
19
+ export declare function normalizeSemanticToolName(raw: string | undefined): string;
20
+
21
+ export declare function resolveOpenDocEvent(toolName: string | undefined, args: Record<string, unknown> | undefined, result: unknown): OpenDocEvent | null;
@@ -0,0 +1,2 @@
1
+ const d={pencil_open_document:"pencil",pencil_close_document:"pencil",pencil_delete_document:"pencil",draw_open_document:"draw",draw_close_document:"draw",draw_delete_document:"draw",office_open_document:"office",office_close_document:"office",office_delete_document:"office"};function s(t){let e=String(t??"").trim().toLowerCase();if(!e)return"";const n=e.lastIndexOf("/");return n>=0&&(e=e.slice(n+1)),e.includes("__")&&(e=e.split("__").pop()??e),e}function p(t,e){if(t!=="call_tool"||!e)return null;const n=e.name;if(typeof n!="string"||!n.trim())return null;const r=e.arguments&&typeof e.arguments=="object"&&!Array.isArray(e.arguments)?e.arguments:{};return{innerName:s(n),innerArgs:r}}function a(t){const e=String(t??"").trim().replace(/\\/g,"/"),n=e.lastIndexOf("/");return n>=0?e.slice(n+1):e}function _(t){if(t&&typeof t=="object"&&!Array.isArray(t))return t;if(typeof t!="string")return null;try{const e=JSON.parse(t);return!e||typeof e!="object"||Array.isArray(e)?null:e}catch{return null}}function g(t,e,n){let r=s(t),c=e;if(r==="call_tool"){const u=p(r,e);if(!u)return null;r=u.innerName,c=u.innerArgs}const o=d[r];if(!o)return null;if(r.endsWith("_close_document"))return{action:"close",kind:o};const i=_(n),f=typeof i?.docId=="string"?i.docId.trim():typeof c?.docId=="string"?c.docId.trim():"";if(r.endsWith("_delete_document"))return f?{action:"delete",kind:o,docId:f}:null;if(!i||i.ok===!1)return null;const l=y(r,o,i,c);if(!l)return null;const m=o==="office"&&typeof i.kind=="string"&&i.kind.trim()?i.kind.trim():void 0;return{action:"open",kind:o,docId:f,name:l,...m?{detail:m}:{}}}function y(t,e,n,r){if(e!=="office"){const f=typeof n.name=="string"?n.name.trim():"";return f||(typeof r?.name=="string"?r.name.trim():"")}const c=typeof n.fileName=="string"?n.fileName.trim():"";if(c)return c;const o=typeof n.file=="string"?n.file.trim():"";if(o)return a(o);const i=typeof r?.file=="string"?r.file.trim():"";return i?a(i):""}export{s as normalizeSemanticToolName,g as resolveOpenDocEvent};
2
+
@@ -0,0 +1,24 @@
1
+
2
+
3
+ export type OpenDocKind = "pencil" | "draw" | "office";
4
+
5
+ export interface OpenDocEntry {
6
+
7
+ kind: OpenDocKind;
8
+
9
+ docId: string;
10
+
11
+ name: string;
12
+
13
+ detail?: string;
14
+ }
15
+
16
+ export declare function recordOpenDocument(agentId: string | undefined, entry: OpenDocEntry): void;
17
+
18
+ export declare function closeOpenDocument(agentId: string | undefined, kind: OpenDocKind): void;
19
+
20
+ export declare function removeOpenDocument(agentId: string | undefined, kind: OpenDocKind, docId: string): void;
21
+
22
+ export declare function listOpenDocuments(agentId: string | undefined): OpenDocEntry[];
23
+
24
+ export declare function clearOpenDocuments(agentId: string | undefined): void;
@@ -0,0 +1,2 @@
1
+ const o=new Map;function s(r,t){const e=String(r??"").trim();if(!e||!t.name.trim())return;let n=o.get(e);n||(n=new Map,o.set(e,n)),n.set(t.kind,{...t,docId:t.docId.trim(),name:t.name.trim()})}function f(r,t){const e=String(r??"").trim();e&&o.get(e)?.delete(t)}function g(r,t,e){const n=String(r??"").trim();if(!n)return;const c=e.trim();if(!c)return;const i=o.get(n)?.get(t);i&&i.docId&&i.docId===c&&o.get(n)?.delete(t)}function m(r){const t=String(r??"").trim();if(!t)return[];const e=o.get(t);if(!e)return[];const n=["pencil","draw","office"],c=[];for(const i of n){const u=e.get(i);u&&c.push(u)}return c}function d(r){const t=String(r??"").trim();t&&o.delete(t)}export{d as clearOpenDocuments,f as closeOpenDocument,m as listOpenDocuments,s as recordOpenDocument,g as removeOpenDocument};
2
+
@@ -1,2 +1,2 @@
1
- import{createHash as T}from"node:crypto";import i from"node:fs/promises";import d from"node:path";import{getBridgeDataHome as v}from"../config.js";const p="acode-request-invalid-",I=20,h=10*6e4,_=8e3,w=2*1024*1024,u=800;let m=!1;const g=new Map;function C(){g.clear(),m=!1}function S(){return d.join(v(),"log")}function D(e){return T("sha1").update(e).digest("hex").slice(0,16)}function E(e){return new Date(e).toISOString().replace(/[:.]/g,"-")}function x(e){if(!Array.isArray(e))return{conversation:[],truncated:!1};try{if(JSON.stringify(e).length<=w)return{conversation:e,truncated:!1}}catch{}return{conversation:e.map(n=>{if(!n||typeof n!="object")return{value:String(n).slice(0,u)};const r=n,t=r.content;let o;if(typeof t=="string")o=t.slice(0,u);else if(t==null)o=t;else try{o=JSON.stringify(t).slice(0,u)}catch{o="[unserializable]"}return{role:r.role,tool_call_id:r.tool_call_id,tool_calls:Array.isArray(r.tool_calls)?r.tool_calls.map(c=>{const s=c;return{id:s?.id,type:s?.type,name:s?.name??s?.function?.name}}):void 0,contentPreview:o}}),truncated:!0}}async function O(e){try{const n=(await i.readdir(e)).filter(t=>t.startsWith(p)&&t.endsWith(".json")).sort(),r=n.length-I;if(r<=0)return;await Promise.all(n.slice(0,r).map(t=>i.rm(d.join(e,t),{force:!0})))}catch{}}async function M(e){const a=S(),n=D(e.errorText),r=Date.now(),t=g.get(e.sessionId);if(t&&t.fingerprint===n&&r-t.at<h)return t.file;const{conversation:o,truncated:c}=x(e.conversation),s=`${p}${E(r)}-${e.sessionId.slice(0,8)}.json`,l=d.join(a,s),y={timestamp:new Date(r).toISOString(),kind:"provider-request-invalid",sessionId:e.sessionId,agentId:e.agentId,model:e.model,attempt:e.attempt,errorText:e.errorText.slice(0,_),errorTextTruncated:e.errorText.length>_,messageCount:Array.isArray(e.conversation)?e.conversation.length:0,conversationTruncated:c,conversation:o,...e.extra?{extra:e.extra}:{}};try{return await i.mkdir(a,{recursive:!0}),await i.writeFile(l,JSON.stringify(y,null,2),"utf8"),g.set(e.sessionId,{fingerprint:n,file:l,at:r}),await O(a),l}catch(f){return m||(m=!0,console.warn(`[request-invalid-dump] \u4E0A\u4E0B\u6587\u5FEB\u7167\u5199\u5165\u5931\u8D25\uFF0C\u540E\u7EED\u5FEB\u7167\u5C06\u88AB\u4E22\u5F03: ${f instanceof Error?f.message:String(f)}`)),null}}export{M as dumpInvalidProviderRequest,S as getInvalidRequestDumpDir,C as resetInvalidRequestDumpStateForTest};
1
+ import{createHash as T}from"node:crypto";import i from"node:fs/promises";import d from"node:path";import{getBridgeDataHome as v}from"../config.js";const p="acode-request-invalid-",I=20,h=10*6e4,_=8e3,w=2*1024*1024,u=800;let m=!1;const g=new Map;function C(){g.clear(),m=!1}function S(){return d.join(v(),"log")}function D(e){return T("sha1").update(e).digest("hex").slice(0,16)}function E(e){return new Date(e).toISOString().replace(/[:.]/g,"-")}function x(e){if(!Array.isArray(e))return{conversation:[],truncated:!1};try{if(JSON.stringify(e).length<=w)return{conversation:e,truncated:!1}}catch{}return{conversation:e.map(n=>{if(!n||typeof n!="object")return{value:String(n).slice(0,u)};const r=n,t=r.content;let o;if(typeof t=="string")o=t.slice(0,u);else if(t==null)o=t;else try{o=JSON.stringify(t).slice(0,u)}catch{o="[unserializable]"}return{role:r.role,tool_call_id:r.tool_call_id,tool_calls:Array.isArray(r.tool_calls)?r.tool_calls.map(c=>{const s=c;return{id:s?.id,type:s?.type,name:s?.name??s?.function?.name}}):void 0,contentPreview:o}}),truncated:!0}}async function O(e){try{const n=(await i.readdir(e)).filter(t=>t.startsWith(p)&&t.endsWith(".json")).sort(),r=n.length-I;if(r<=0)return;await Promise.all(n.slice(0,r).map(t=>i.rm(d.join(e,t),{force:!0})))}catch{}}async function M(e){const a=S(),n=D(e.errorText),r=Date.now(),t=g.get(e.sessionId);if(t&&t.fingerprint===n&&r-t.at<h)return t.file;const{conversation:o,truncated:c}=x(e.conversation),s=`${p}${E(r)}-${e.sessionId.slice(0,8)}-${n.slice(0,8)}.json`,l=d.join(a,s),y={timestamp:new Date(r).toISOString(),kind:"provider-request-invalid",sessionId:e.sessionId,agentId:e.agentId,model:e.model,attempt:e.attempt,errorText:e.errorText.slice(0,_),errorTextTruncated:e.errorText.length>_,messageCount:Array.isArray(e.conversation)?e.conversation.length:0,conversationTruncated:c,conversation:o,...e.extra?{extra:e.extra}:{}};try{return await i.mkdir(a,{recursive:!0}),await i.writeFile(l,JSON.stringify(y,null,2),"utf8"),g.set(e.sessionId,{fingerprint:n,file:l,at:r}),await O(a),l}catch(f){return m||(m=!0,console.warn(`[request-invalid-dump] \u4E0A\u4E0B\u6587\u5FEB\u7167\u5199\u5165\u5931\u8D25\uFF0C\u540E\u7EED\u5FEB\u7167\u5C06\u88AB\u4E22\u5F03: ${f instanceof Error?f.message:String(f)}`)),null}}export{M as dumpInvalidProviderRequest,S as getInvalidRequestDumpDir,C as resetInvalidRequestDumpStateForTest};
2
2
 
@@ -1,3 +1,3 @@
1
- import d from"axios";import{getBrowserPool as m}from"../browser/browser-pool.js";import{getAcodeBgManager as f}from"./acode-bg-manager.js";import{resolveSchedulerBaseUrl as g}from"../config.js";import{ptySessions as p}from"../routes/pty.js";import{getRuntimeAccessToken as l}from"./runtime-binding.js";import{subAgentManager as h}from"./sub-agent-manager-instance.js";import{createLogger as $}from"../utils/logger.js";const c=$("runtime-resources"),w=3e3,i=20;function s(r,e){const t=String(r??"").replace(/\s+/g," ").trim();return t.length>e?`${t.slice(0,e)}\u2026`:t}function b(r){if(typeof r!="number"||!Number.isFinite(r)||r<=0)return"";const e=new Date(r),t=n=>String(n).padStart(2,"0");return`${t(e.getHours())}:${t(e.getMinutes())}`}async function k(r){try{const e=await f();return e?.listTasksWithReconcile?(await e.listTasksWithReconcile(r,"running")).filter(n=>n.status==="running").slice(0,i).map(n=>{const o=b(n.startedAt),a=n.description?s(n.description,60):s(n.commandPreview||n.command,80);return{kind:"background commands",text:`${n.taskId} \`${a}\`${o?` (since ${o})`:""}`}}):[]}catch(e){return c.warn(`collect background commands failed: ${e?.message}`),[]}}function y(r){try{const e=[];for(const t of p.values()){if(t.ownerUserId!==r||t.status!=="running")continue;if(e.length>=i)break;const n=s(t.title||t.shell,60);e.push({kind:"terminals",text:`${t.id} ${n} (running)`})}return e}catch(e){return c.warn(`collect terminals failed: ${e?.message}`),[]}}function S(r){try{return h.listActiveByParent(r).slice(0,i).map(t=>({kind:"sub-agents",text:`${t.subId} ${t.subagentType} "${s(t.description,60)}" (running)`}))}catch(e){return c.warn(`collect sub-agents failed: ${e?.message}`),[]}}function v(r){try{return m().listSessions(r).slice(0,i).map(t=>{const n=s(t.title||t.browserId,40),o=s(t.currentUrl,80);return{kind:"browsers",text:`"${n}"${o?` ${o}`:""}`}})}catch(e){return c.warn(`collect browsers failed: ${e?.message}`),[]}}function u(r,e){if(!Array.isArray(e))return[];const t=[];for(const n of e){if(t.length>=i)break;const o=s(String(n?.name??n?.docId??""),60);if(!o)continue;const a=String(n?.permission??"").trim();t.push({kind:r,text:`${o}${a?` (${a})`:""}`})}return t}async function x(r){try{const{url:e}=g();if(!e)return[];const t=l(e)||l();if(!t)return[];const o=(await d.get(`${e}/api/runtime/agent-resources`,{params:{agentId:r},headers:{"X-Runtime-Token":t},timeout:w})).data??{};return[...u("office docs",o.officeDocs),...u("pencil docs",o.pencilDocs),...u("draw docs",o.drawDocs)]}catch(e){return c.warn(`collect java docs failed (office/pencil/draw omitted): agentId=${r}, error=${e?.message}`),[]}}function A(r){const e=new Map;for(const n of r){const o=e.get(n.kind);o?o.push(n.text):e.set(n.kind,[n.text])}const t=["[Runtime resources \u2014 live snapshot taken after context compaction.]","These already exist and are still running/available. Reuse them instead of creating duplicates","(do not start another dev server, open another browser, or redraw an existing document).","Details (output, page content, document body) are not included \u2014 query them when needed."];for(const[n,o]of e)t.push(`- ${n} (${o.length}): ${o.join("; ")}`);return t.join(`
2
- `)}async function j(r){if(!r)return null;try{const[e,t]=await Promise.all([k(r),x(r)]),n=[...e,...y(r),...S(r),...v(r),...t];return n.length===0?null:A(n)}catch(e){return c.warn(`build snapshot failed: ${e?.message}`),null}}export{j as buildRuntimeResourcesSnapshot};
1
+ import m from"axios";import{getBrowserPool as f}from"../browser/browser-pool.js";import{resolveSchedulerBaseUrl as p}from"../config.js";import{ptySessions as g}from"../routes/pty.js";import{createLogger as $}from"../utils/logger.js";import{getAcodeBgManager as h}from"./acode-bg-manager.js";import{agentPtyManager as w}from"./agent-pty-manager.js";import{listOpenDocuments as b}from"./open-doc-registry.js";import{getRuntimeAccessToken as l}from"./runtime-binding.js";import{subAgentManager as k}from"./sub-agent-manager-instance.js";const i=$("runtime-resources"),S=3e3,a=20,y={pencil:"open pencil canvas",draw:"open draw diagram",office:"open office document"};function c(t,e){const n=String(t??"").replace(/\s+/g," ").trim();return n.length>e?`${n.slice(0,e)}\u2026`:n}function D(t){if(typeof t!="number"||!Number.isFinite(t)||t<=0)return"";const e=new Date(t),n=r=>String(r).padStart(2,"0");return`${n(e.getHours())}:${n(e.getMinutes())}`}async function v(t){try{const e=await h();return e?.listTasksWithReconcile?(await e.listTasksWithReconcile(t,"running")).filter(r=>r.status==="running").slice(0,a).map(r=>{const o=D(r.startedAt),s=r.description?c(r.description,60):c(r.commandPreview||r.command,80);return{kind:"background commands",text:`${r.taskId} \`${s}\`${o?` (since ${o})`:""}`}}):[]}catch(e){return i.warn(`collect background commands failed: ${e?.message}`),[]}}function x(t){try{const e=[];let n=[];try{n=w.listWaitingForInput(t)}catch(o){i.warn(`collect terminals waiting-input failed: ${o?.message}`)}const r=new Set(n);for(const o of g.values()){if(o.ownerUserId!==t||o.status!=="running")continue;if(e.length>=a)break;const s=c(o.title||o.shell,60),d=r.has(o.id)?"running, waiting for input":"running";e.push({kind:"terminals",text:`${o.id} ${s} (${d})`})}return e}catch(e){return i.warn(`collect terminals failed: ${e?.message}`),[]}}function A(t){try{return k.listActiveByParent(t).slice(0,a).map(n=>({kind:"sub-agents",text:`${n.subId} ${n.subagentType} "${c(n.description,60)}" (running)`}))}catch(e){return i.warn(`collect sub-agents failed: ${e?.message}`),[]}}function T(t){try{return f().listSessions(t).slice(0,a).map(n=>{const r=c(n.title||n.browserId,40),o=c(n.currentUrl,80);return{kind:"browsers",text:`${c(n.browserId,40)} "${r}"${o?` ${o}`:""}`}})}catch(e){return i.warn(`collect browsers failed: ${e?.message}`),[]}}function M(t){try{return b(t).map(e=>{const n=e.docId?`${e.docId} `:"",r=e.detail?` (${e.detail})`:"";return{kind:y[e.kind],text:`${n}${e.name}${r}`}})}catch(e){return i.warn(`collect open documents failed: ${e?.message}`),[]}}function u(t,e){if(!Array.isArray(e))return[];const n=[];for(const r of e){if(n.length>=a)break;const o=c(String(r?.name??r?.docId??""),60);if(!o)continue;const s=String(r?.permission??"").trim();n.push({kind:t,text:`${o}${s?` (${s})`:""}`})}return n}async function R(t){try{const{url:e}=p();if(!e)return[];const n=l(e)||l();if(!n)return[];const o=(await m.get(`${e}/api/runtime/agent-resources`,{params:{agentId:t},headers:{"X-Runtime-Token":n},timeout:S})).data??{};return[...u("office docs",o.officeDocs),...u("pencil docs",o.pencilDocs),...u("draw docs",o.drawDocs)]}catch(e){return i.warn(`collect java docs failed (office/pencil/draw omitted): agentId=${t}, error=${e?.message}`),[]}}function _(t){const e=new Map;for(const r of t){const o=e.get(r.kind);o?o.push(r.text):e.set(r.kind,[r.text])}const n=["[Runtime resources \u2014 live snapshot taken after context compaction.]","These already exist and are still running/available. Reuse them instead of creating duplicates","(do not start another dev server, open another browser, or redraw an existing document).","Details (output, page content, document body) are not included \u2014 query them when needed."];for(const[r,o]of e)n.push(`- ${r} (${o.length}): ${o.join("; ")}`);return n.join(`
2
+ `)}async function W(t){if(!t)return null;try{const[e,n]=await Promise.all([v(t),R(t)]),r=[...e,...x(t),...A(t),...T(t),...M(t),...n];if(r.length===0)return null;const o=_(r);return i.debug(`snapshot built for agentId=${t}: ${r.length} entries (${[...new Set(r.map(s=>s.kind))].join(", ")})`),o}catch(e){return i.warn(`build snapshot failed: ${e?.message}`),null}}export{W as buildRuntimeResourcesSnapshot};
3
3
 
@@ -0,0 +1,7 @@
1
+
2
+
3
+ export declare function setSnapshotRetentionDays(days: unknown, fromConfig?: boolean): void;
4
+
5
+ export declare function getSnapshotRetentionDays(): number | null;
6
+
7
+ export declare function resetSnapshotRetentionForTest(): void;
@@ -0,0 +1,2 @@
1
+ const u=3650;let t=null,l=!1;function i(e,o=!1){if(o&&l)return;if(o||(l=!0),e==null||e===""){t=null;return}if(typeof e=="boolean"){t=null;return}const n=Number(e);if(!Number.isFinite(n)||n<=0){t=null;return}t=Math.min(Math.floor(n),3650)}function r(){return t}function s(){t=null,l=!1}export{r as getSnapshotRetentionDays,s as resetSnapshotRetentionForTest,i as setSnapshotRetentionDays};
2
+
@@ -0,0 +1,31 @@
1
+
2
+
3
+ export type LaunchStackType = "generic" | "java" | "python" | "node" | "go" | "php";
4
+ export interface TechStackCandidate {
5
+
6
+ id: string;
7
+
8
+ name: string;
9
+ type: LaunchStackType;
10
+
11
+ command: string;
12
+
13
+ cwd: string;
14
+
15
+ relativeCwd: string;
16
+
17
+ stack: string;
18
+
19
+ markers: string[];
20
+ confidence: "high" | "medium" | "low";
21
+ }
22
+ export interface TechStackScanResult {
23
+ workspacePath: string;
24
+
25
+ scannedDirs: number;
26
+
27
+ truncated: boolean;
28
+ candidates: TechStackCandidate[];
29
+ }
30
+
31
+ export declare function scanTechStack(workspacePath: string): Promise<TechStackScanResult>;
@@ -0,0 +1,2 @@
1
+ import N from"node:crypto";import{promises as k}from"node:fs";import d from"node:path";import{createLogger as R}from"../utils/logger.js";import{isIgnoredByGitIgnore as A,loadGitIgnoreRules as B}from"./gitignore.js";const w=R("tech-stack-scanner"),E=64*1024,T=4e3,C=2e3,F=5,M=new Set([".git",".svn",".hg",".idea",".vscode","node_modules","bower_components","vendor","target","dist","build","out","bin","obj",".gradle",".venv","venv","env","__pycache__",".mypy_cache",".pytest_cache",".next",".nuxt",".svelte-kit",".turbo",".cache","coverage"]);function q(e,n){return e==="."?n:`${e}/${n}`}async function g(e){try{const n=await k.open(e,"r");try{const t=await n.stat();if(!t.isFile())return null;const p=Math.min(t.size,E),o=Buffer.alloc(p);return await n.read(o,0,p,0),o.toString("utf8")}finally{await n.close()}}catch{return null}}async function D(e){try{return(await k.stat(e)).isFile()}catch{return!1}}function _(e){if(!e)return null;try{const n=JSON.parse(e);return n&&typeof n=="object"?n:null}catch{return null}}function L(e,n,t){return N.createHash("sha1").update(`${e}|${n}|${t}`).digest("hex").slice(0,12)}function I(e){return process.platform==="win32"?`${e}.cmd`:`./${e}`}function O(e,n,t){return n.has("mvnw")||n.has("mvnw.cmd")?`${I("mvnw")} ${t}`:`mvn ${t}`}function W(e,n){return e.has("gradlew")||e.has("gradlew.bat")?`${I("gradlew")} ${n}`:`gradle ${n}`}function G(e){if(!e)return"";const t=e.replace(/<parent>[\s\S]*?<\/parent>/gi,"").match(/<artifactId>\s*([^<\s]+)\s*<\/artifactId>/i);return t?t[1].trim():""}function J(e){if(!e)return"";const n=e.match(/rootProject\.name\s*=\s*['"]([^'"]+)['"]/);return n?n[1].trim():""}function X(e){if(!e)return"";const n=e.match(/\[package\][\s\S]*?(?=\n\[|$)/),p=(n?n[0]:e).match(/^\s*name\s*=\s*["']([^"']+)["']/m);return p?p[1].trim():""}function x(e){return d.basename(e)||"workspace"}function Y(e){return e.has("pnpm-lock.yaml")||e.has("pnpm-workspace.yaml")?{runner:"pnpm",runCommand:n=>`pnpm run ${n}`}:e.has("yarn.lock")?{runner:"yarn",runCommand:n=>`yarn run ${n}`}:e.has("bun.lockb")||e.has("bun.lock")?{runner:"bun",runCommand:n=>`bun run ${n}`}:{runner:"npm",runCommand:n=>`npm run ${n}`}}const K=["dev","develop","serve","start"];function z(e){for(const n of K)if(typeof e[n]=="string"&&e[n].trim())return n;return null}function H(e,n){return e.has("uv.lock")?"uv run ":n&&/\[tool\.poetry\]/i.test(n)?"poetry run ":""}function m(e){const{facts:n,stack:t,type:p,command:o,name:i,markers:h,confidence:f}=e;return{id:L(n.relativeDir,t,o),name:i||x(n.absoluteDir),type:p,command:o,cwd:n.absoluteDir,relativeCwd:n.relativeDir,stack:t,markers:h,confidence:f}}async function Q(e){const{absoluteDir:n,entryNames:t,subdirectories:p}=e,o=[],i=x(n);if(t.has("pom.xml")){const a=await g(d.join(n,"pom.xml")),s=G(a)||i,c=!!(a&&/spring-boot/i.test(a));o.push(m({facts:e,stack:c?"spring-boot":"maven",type:"java",command:O(n,t,c?"spring-boot:run":"-DskipTests package"),name:s,markers:["pom.xml"],confidence:c?"high":"medium"}))}else if(t.has("build.gradle")||t.has("build.gradle.kts")){const a=t.has("build.gradle.kts")?"build.gradle.kts":"build.gradle",s=await g(d.join(n,a)),c=J(s)||i,r=!!(s&&/org\.springframework\.boot/i.test(s));o.push(m({facts:e,stack:r?"spring-boot":"gradle",type:"java",command:W(t,r?"bootRun":"build"),name:c,markers:[a],confidence:r?"high":"medium"}))}if(t.has("package.json")){const a=_(await g(d.join(n,"package.json"))),s=a&&typeof a.scripts=="object"&&a.scripts!==null?a.scripts:{},c=z(s);if(c){const{runCommand:r}=Y(t),l=a&&typeof a.name=="string"?a.name.trim():"";o.push(m({facts:e,stack:"node",type:"node",command:r(c),name:l||i,markers:["package.json"],confidence:"high"}))}}if(t.has("manage.py"))o.push(m({facts:e,stack:"django",type:"python",command:"python manage.py runserver",name:`${i} (Django)`,markers:["manage.py"],confidence:"high"}));else if(t.has("pyproject.toml")||t.has("requirements.txt")||t.has("setup.py")){const a=t.has("pyproject.toml")?await g(d.join(n,"pyproject.toml")):null,s=H(t,a),c=t.has("pyproject.toml")?"pyproject.toml":t.has("requirements.txt")?"requirements.txt":"setup.py";!!(a&&/fastapi/i.test(a))&&t.has("main.py")?o.push(m({facts:e,stack:"fastapi",type:"python",command:`${s}uvicorn main:app --reload`,name:`${i} (FastAPI)`,markers:[c,"main.py"],confidence:"high"})):t.has("main.py")?o.push(m({facts:e,stack:"python",type:"python",command:`${s}python main.py`,name:i,markers:[c,"main.py"],confidence:"medium"})):t.has("app.py")&&o.push(m({facts:e,stack:"python",type:"python",command:`${s}python app.py`,name:i,markers:[c,"app.py"],confidence:"medium"}))}if(t.has("go.mod")){if(t.has("main.go"))o.push(m({facts:e,stack:"go",type:"go",command:"go run .",name:i,markers:["go.mod","main.go"],confidence:"high"}));else if(p.includes("cmd")){const a=d.join(n,"cmd");let s=[];try{s=await k.readdir(a,{withFileTypes:!0})}catch{s=[]}const c=s.filter(r=>r.isDirectory()).map(r=>r.name).sort().slice(0,F);for(const r of c)await D(d.join(a,r,"main.go"))&&o.push(m({facts:e,stack:"go",type:"go",command:`go run ./cmd/${r}`,name:`${i}/${r}`,markers:["go.mod",`cmd/${r}/main.go`],confidence:"high"}))}}if(t.has("composer.json")){const a=_(await g(d.join(n,"composer.json"))),s=a&&typeof a.name=="string"?a.name.trim():"";!!(a&&(/laravel\/framework/i.test(JSON.stringify(a.require??{}))||/laravel\/framework/i.test(JSON.stringify(a["require-dev"]??{}))))&&t.has("artisan")?o.push(m({facts:e,stack:"laravel",type:"php",command:"php artisan serve",name:s||i,markers:["composer.json","artisan"],confidence:"high"})):e.directoryNames.has("public")&&e.directoryNames.has("bin")?o.push(m({facts:e,stack:"symfony",type:"php",command:"php -S localhost:8000 -t public",name:s||i,markers:["composer.json"],confidence:"medium"})):t.has("index.php")&&o.push(m({facts:e,stack:"php",type:"php",command:"php -S localhost:8000",name:s||i,markers:["composer.json","index.php"],confidence:"medium"}))}else t.has("index.php")&&o.push(m({facts:e,stack:"php",type:"php",command:"php -S localhost:8000",name:i,markers:["index.php"],confidence:"low"}));if(t.has("Cargo.toml")){const a=await g(d.join(n,"Cargo.toml")),s=!!(a&&/^\s*\[package\]/m.test(a)),c=!!(a&&/^\s*\[\[bin\]\]/m.test(a)||await D(d.join(n,"src","main.rs")));if(s&&c){const r=X(a);o.push(m({facts:e,stack:"cargo",type:"generic",command:"cargo run",name:r||i,markers:["Cargo.toml"],confidence:"medium"}))}}const h=t.has("deno.json")?"deno.json":t.has("deno.jsonc")?"deno.jsonc":"";if(h){const a=await g(d.join(n,h));a&&/["']?start["']?\s*:/.test(a)&&o.push(m({facts:e,stack:"deno",type:"generic",command:"deno task start",name:i,markers:[h],confidence:"medium"}))}const f=["docker-compose.yml","docker-compose.yaml","compose.yml","compose.yaml"].find(a=>t.has(a));return f&&o.push(m({facts:e,stack:"docker-compose",type:"generic",command:"docker compose up",name:`${i} (compose)`,markers:[f],confidence:"medium"})),o}function U(e,n,t,p){return M.has(t.toLowerCase())?!0:p.length===0?!1:A(n,!0,p)}async function ae(e){const n=String(e||"").trim();if(!n)throw new Error("workspacePath is required");const t=d.resolve(n);if(!(await k.stat(t)).isDirectory())throw new Error(`Workspace path is not a directory: ${t}`);const o=await B(t),i=[];let h=0,f=!1;const a=[[t,"."]];for(;a.length>0;){if(h>=T){f=!0;break}const r=a.shift();if(!r)break;const[l,v]=r;h+=1;let y=[];try{y=await k.readdir(l,{withFileTypes:!0})}catch(u){w.warn(`[scan] readdir failed: dir=${l} error=${u.message}`);continue}y.length>C&&(f=!0,y=y.slice(0,C));const b=new Set,j=new Set,$=[];for(const u of y)if(u.isDirectory()){j.add(u.name);const S=q(v,u.name);if(U(t,S,u.name,o))continue;$.push(u.name),a.push([d.join(l,u.name),S])}else u.isFile()&&b.add(u.name);const P={absoluteDir:l,relativeDir:v,entryNames:b,directoryNames:j,subdirectories:$};try{i.push(...await Q(P))}catch(u){w.warn(`[scan] detect failed: dir=${l} error=${u.message}`)}}const s=new Set,c=i.filter(r=>s.has(r.id)?!1:(s.add(r.id),!0));return c.sort((r,l)=>r.relativeCwd!==l.relativeCwd?r.relativeCwd.localeCompare(l.relativeCwd):r.stack.localeCompare(l.stack)),w.info(`[scan] workspace=${t} dirs=${h} candidates=${c.length} truncated=${f}`),{workspacePath:t,scannedDirs:h,truncated:f,candidates:c}}export{ae as scanTechStack};
2
+
@@ -1,4 +1,36 @@
1
1
 
2
+ import WebSocket from "ws";
3
+ interface LocalStream {
4
+ ws: WebSocket;
5
+
6
+ pending: Array<{
7
+ data: Buffer;
8
+ bin: boolean;
9
+ }>;
10
+ }
11
+ interface TunnelState {
12
+
13
+ serverUrl: string;
14
+ instanceId: string;
15
+ ws?: WebSocket;
16
+
17
+ streams: Map<string, LocalStream>;
18
+
19
+ pendingReqChunks: Map<string, PendingRequestChunks>;
20
+ timer?: NodeJS.Timeout;
21
+ reconnectTimer?: NodeJS.Timeout;
22
+
23
+ reconnectAttempts?: number;
24
+ shuttingDown: boolean;
25
+ }
26
+
27
+ interface PendingRequestChunks {
28
+ method: string;
29
+ path: string;
30
+ headers?: Record<string, string[]>;
31
+ parts: Buffer[];
32
+ timer: NodeJS.Timeout;
33
+ }
2
34
 
3
35
  export declare function startTunnelClient(serverUrl: string, instanceId: string): void;
4
36
 
@@ -7,3 +39,13 @@ export declare function stopTunnelClient(): void;
7
39
  export declare function stopTunnelClientForServer(serverUrl: string): void;
8
40
 
9
41
  export declare function stopTunnelClientsExcept(serverUrls: string[]): void;
42
+
43
+ export declare const __tunnelClientTestHooks: {
44
+ createState(serverUrl?: string, instanceId?: string): TunnelState;
45
+ dispatch(state: TunnelState, msg: Record<string, unknown>): void;
46
+
47
+ pendingChunkIds(state: TunnelState): string[];
48
+
49
+ cleanup(state: TunnelState): void;
50
+ };
51
+ export {};
@@ -1,2 +1,2 @@
1
- import I,{AxiosError as O}from"axios";import p from"ws";import{getActiveBridgeHttpPort as _}from"../config.js";import{logger as i}from"../utils/logger.js";import{loadConfigs as x,loadPrimaryLifecycleConfig as D}from"./auto-register.js";import{getRuntimeAccessToken as L,normalizeSchedulerBaseUrl as S}from"./runtime-binding.js";const W=15e3,P=5e3,$=3e4,k=.2,J=200,q=1*1024*1024,G=4001,H=768*1024,K=384*1024,h=new Map;function X(e){const r=e.serverUrl,n=e.instanceId;if(!r||!n)return null;try{const t=new URL(r),o=t.protocol==="https:"?"wss:":"ws:",c=L(r),l=x().find(m=>S(m.serverUrl)===r)||D(),s=c||l.userKey;if(c?i.debug("[TunnelClient] \u4F7F\u7528 runtime access token \u8FDE\u63A5\u96A7\u9053"):l.userKey&&i.warn("[TunnelClient] getRuntimeAccessToken \u8FD4\u56DE\u7A7A\uFF0C\u56DE\u9000\u5230 userKey\uFF08\u975E runtime token\uFF0C\u9274\u6743\u53EF\u80FD\u5931\u8D25\uFF09: targetUrl=",r),!s)return i.warn("[TunnelClient] \u7F3A\u5C11 runtime token\uFF0C\u65E0\u6CD5\u5EFA\u7ACB\u96A7\u9053: targetUrl=",r),null;const a=new URLSearchParams({instanceId:n});return{url:`${o}//${t.host}/ws/bridge-tunnel?${a.toString()}`,token:s}}catch(t){return i.warn("[TunnelClient] \u65E0\u6CD5\u89E3\u6790 serverUrl:",String(t)),null}}function oe(e,r){const n=S(e);if(!n){i.warn("[TunnelClient] serverUrl \u65E0\u6548\uFF0C\u65E0\u6CD5\u542F\u52A8\u96A7\u9053:",e);return}const t=h.get(n);t&&(t.shuttingDown=!0,C(t),h.delete(n));const o={serverUrl:n,instanceId:r,streams:new Map,shuttingDown:!1};h.set(n,o),A(o)}function se(){for(const e of h.values())e.shuttingDown=!0,C(e);h.clear()}function ie(e){const r=S(e);if(!r)return;const n=h.get(r);n&&(n.shuttingDown=!0,C(n),h.delete(r),i.info(`[TunnelClient] tunnel stopped: ${r}`))}function ce(e){const r=new Set(e.map(n=>S(n)).filter(n=>!!n));for(const[n,t]of h.entries())r.has(n)||(i.info(`[TunnelClient] serverUrl \u5DF2\u4E0D\u5728\u914D\u7F6E\u4E2D\uFF0C\u505C\u6B62\u65E7\u96A7\u9053: ${n}`),t.shuttingDown=!0,C(t),h.delete(n))}function C(e){e.timer&&(clearInterval(e.timer),e.timer=void 0),e.reconnectTimer&&(clearTimeout(e.reconnectTimer),e.reconnectTimer=void 0);for(const[r,n]of e.streams.entries()){try{n.ws.close(1e3,"tunnel client stopped")}catch{}e.streams.delete(r)}if(e.ws){e.ws.removeAllListeners();try{e.ws.close(1e3,"tunnel client stopped")}catch{}e.ws=void 0}}function A(e){if(e.shuttingDown)return;const r=e.instanceId;if(!r)return;const n=X(e);if(!n){b(e);return}i.info("[TunnelClient] \u8FDE\u63A5\u96A7\u9053\u670D\u52A1\u7AEF:",n.url);const t=new p(n.url,{handshakeTimeout:15e3,headers:{"X-Runtime-Token":n.token}});e.ws=t,t.on("open",()=>{i.info("[TunnelClient] \u96A7\u9053\u8FDE\u63A5\u5DF2\u5EFA\u7ACB: server=",e.serverUrl,"instanceId=",r),e.reconnectTimer&&clearTimeout(e.reconnectTimer),e.reconnectTimer=void 0,e.reconnectAttempts=0,e.timer=setInterval(()=>{t.readyState===p.OPEN&&t.send(JSON.stringify({t:"ping"}))},W)}),t.on("unexpected-response",(o,c)=>{i.error("[TunnelClient] \u670D\u52A1\u7AEF\u62D2\u7EDD\u8FDE\u63A5\uFF08\u975E 101 \u54CD\u5E94\uFF09: server=",e.serverUrl,"statusCode=",c.statusCode,"statusMessage=",c.statusMessage)}),t.on("upgrade",o=>{i.debug("[TunnelClient] \u6536\u5230\u5347\u7EA7\u54CD\u5E94: statusCode=",o.statusCode)}),t.on("message",o=>{let c;typeof o=="string"?c=o:Buffer.isBuffer(o)?c=o.toString("utf-8"):c=Buffer.from(o).toString("utf-8");try{const l=JSON.parse(c);Y(e,l)}catch(l){i.warn("[TunnelClient] \u89E3\u6790\u96A7\u9053\u6D88\u606F\u5931\u8D25:",String(l))}}),t.on("close",(o,c)=>{if(i.warn("[TunnelClient] \u96A7\u9053\u8FDE\u63A5\u5173\u95ED: server=",e.serverUrl,"code=",o,"reason=",String(c)),B(e),o===G){i.warn("[TunnelClient] \u9274\u6743\u5931\u8D25\uFF084001\uFF09\uFF0C\u505C\u6B62\u81EA\u52A8\u91CD\u8FDE\uFF1B\u5F85\u91CD\u65B0\u6CE8\u518C\u540E\u518D\u542F\u52A8\u96A7\u9053: server=",e.serverUrl);return}b(e)}),t.on("error",o=>{i.warn("[TunnelClient] \u96A7\u9053\u8FDE\u63A5\u9519\u8BEF: server=",e.serverUrl,"err=",o.message,"code=",o.code),B(e),b(e)})}function B(e){e.timer&&(clearInterval(e.timer),e.timer=void 0);for(const[r,n]of e.streams.entries()){try{n.ws.close(1011,"tunnel disconnected")}catch{}e.streams.delete(r)}e.ws=void 0}function b(e){if(e.shuttingDown||e.reconnectTimer)return;const r=(e.reconnectAttempts??0)+1;e.reconnectAttempts=r;const n=Math.min(P*Math.pow(2,r-1),$),t=1-k+Math.random()*k*2,o=Math.round(n*t);i.debug("[TunnelClient] \u8BA1\u5212\u91CD\u8FDE: server=",e.serverUrl,"attempt=",r,"delayMs=",o),e.reconnectTimer=setTimeout(()=>{e.reconnectTimer=void 0,A(e)},o)}function Y(e,r){switch(r.t){case"req":j(e,r);break;case"wsopen":z(e,r);break;case"wsmsg":F(e,r);break;case"wsclose":V(e,r);break;case"ping":f(e,{t:"pong"});break;case"pong":break;default:i.debug("[TunnelClient] \u672A\u77E5\u6D88\u606F\u7C7B\u578B: t=",r.t)}}async function j(e,r){const n=r.id,t=r.method||"GET",o=r.path||"/",c=`http://127.0.0.1:${_()}${o}`;try{const l=r.body?Buffer.from(r.body,"base64"):void 0,s={};if(r.headers)for(const[d,u]of Object.entries(r.headers))u&&u.length>0&&(s[d]=u[0]);s["x-bridge-tunnel-source"]=e.serverUrl;const a=await I.request({url:c,method:t,headers:s,data:l,timeout:0,responseType:"stream",validateStatus:()=>!0}),m={};for(const[d,u]of Object.entries(a.headers))u!==void 0&&(m[d]=Array.isArray(u)?u:[String(u)]);const v=a.data,M=String(m["content-type"]?.[0]||"").toLowerCase().includes("text/event-stream"),g=[];let w=0,T=!1,y=!1;try{if(v)for await(const d of v){const u=Buffer.isBuffer(d)?d:Buffer.from(d);if(u.length!==0){if(T){if(!E(e,n,u))return}else if(M){if(T=!0,y=!0,!f(e,{t:"res",id:n,status:a.status,headers:m,body:null,chunked:!0})||!E(e,n,u))return}else if(g.push(u),w+=u.length,w>H){if(T=!0,y=!0,!f(e,{t:"res",id:n,status:a.status,headers:m,body:null,chunked:!0}))return;for(const R of g)if(!E(e,n,R))return;g.length=0,w=0}}}}catch(d){const u=d instanceof Error?d.message:String(d);i.warn("[TunnelClient] \u672C\u5730\u6D41\u8BFB\u53D6\u4E2D\u65AD:",t,o,"->",u),y?f(e,{t:"resend",id:n,error:u}):f(e,{t:"res",id:n,status:502,headers:{"content-type":["text/plain; charset=utf-8"]},body:Buffer.from(u).toString("base64")});return}if(T){f(e,{t:"resend",id:n});return}const N=w>0?Buffer.concat(g).toString("base64"):"";f(e,{t:"res",id:n,status:a.status,headers:m,body:N||null})}catch(l){const s=l instanceof O?l.message:String(l);i.warn("[TunnelClient] \u672C\u5730\u8F6C\u53D1\u5931\u8D25:",t,o,"->",s),f(e,{t:"res",id:n,status:502,headers:{"content-type":["text/plain; charset=utf-8"]},body:Buffer.from(s).toString("base64")})}}function E(e,r,n){let t=0;for(;t<n.length;){const o=n.subarray(t,t+K);if(!f(e,{t:"reschunk",id:r,d:o.toString("base64")}))return!1;t+=o.length}return!0}function z(e,r){const n=r.id,t=r.path||"/",o=`ws://127.0.0.1:${_()}${t}`;let c;try{c=new p(o,{handshakeTimeout:1e4})}catch(s){i.warn("[TunnelClient] \u672C\u5730 WS \u8FDE\u63A5\u5931\u8D25:",String(s)),f(e,{t:"wsclose",id:n,code:1011,reason:"local ws connect failed"});return}const l={ws:c,pending:[]};e.streams.set(n,l),c.on("open",()=>{for(const s of l.pending)U(l,s.data,s.bin);l.pending=[],f(e,{t:"wsopened",id:n})}),c.on("message",(s,a)=>{const m=Buffer.isBuffer(s)?s:Buffer.from(s);a&&e.ws&&e.ws.bufferedAmount>q||f(e,{t:"wsmsg",id:n,bin:a,d:m.toString("base64")})}),c.on("close",(s,a)=>{e.streams.delete(n),f(e,{t:"wsclose",id:n,code:s,reason:String(a)})}),c.on("error",s=>{i.warn("[TunnelClient] \u672C\u5730 WS \u9519\u8BEF: id=",n,"err=",s.message),e.streams.delete(n);try{c.close(1011,s.message)}catch{}f(e,{t:"wsclose",id:n,code:1011,reason:s.message})})}function U(e,r,n){n?e.ws.send(r):e.ws.send(r.toString("utf-8"))}function F(e,r){const n=e.streams.get(r.id);if(!n)return;const t=r.d?Buffer.from(r.d,"base64"):Buffer.alloc(0);if(n.ws.readyState!==p.OPEN){n.pending.length>=J&&(n.pending.shift(),i.warn("[TunnelClient] \u672C\u5730 WS \u672A\u5C31\u7EEA\uFF0C\u6392\u961F\u6D88\u606F\u8D85\u4E0A\u9650\uFF0C\u4E22\u5F03\u6700\u65E7\u4E00\u6761: id=",r.id)),n.pending.push({data:t,bin:!!r.bin});return}U(n,t,!!r.bin)}function V(e,r){const n=e.streams.get(r.id);if(n){e.streams.delete(r.id);try{n.ws.close(r.code??1e3,r.reason||"")}catch{}}}function f(e,r){if(!e.ws||e.ws.readyState!==p.OPEN)return!1;try{return e.ws.send(JSON.stringify(r)),!0}catch(n){return i.warn("[TunnelClient] sendJson \u5931\u8D25:",String(n)),!1}}export{oe as startTunnelClient,se as stopTunnelClient,ie as stopTunnelClientForServer,ce as stopTunnelClientsExcept};
1
+ import O,{AxiosError as x}from"axios";import g from"ws";import{getActiveBridgeHttpPort as R}from"../config.js";import{logger as s}from"../utils/logger.js";import{loadConfigs as L,loadPrimaryLifecycleConfig as P}from"./auto-register.js";import{getRuntimeAccessToken as W,normalizeSchedulerBaseUrl as C}from"./runtime-binding.js";const $=15e3,G=5e3,H=3e4,A=.2,J=200,K=1*1024*1024,X=4001,Y=768*1024,j=384*1024,z=5*6e4,p=new Map;function F(e){const n=e.serverUrl,r=e.instanceId;if(!n||!r)return null;try{const t=new URL(n),o=t.protocol==="https:"?"wss:":"ws:",u=W(n),l=L().find(h=>C(h.serverUrl)===n)||P(),i=u||l.userKey;if(u?s.debug("[TunnelClient] \u4F7F\u7528 runtime access token \u8FDE\u63A5\u96A7\u9053"):l.userKey&&s.warn("[TunnelClient] getRuntimeAccessToken \u8FD4\u56DE\u7A7A\uFF0C\u56DE\u9000\u5230 userKey\uFF08\u975E runtime token\uFF0C\u9274\u6743\u53EF\u80FD\u5931\u8D25\uFF09: targetUrl=",n),!i)return s.warn("[TunnelClient] \u7F3A\u5C11 runtime token\uFF0C\u65E0\u6CD5\u5EFA\u7ACB\u96A7\u9053: targetUrl=",n),null;const f=new URLSearchParams({instanceId:r});return{url:`${o}//${t.host}/ws/bridge-tunnel?${f.toString()}`,token:i}}catch(t){return s.warn("[TunnelClient] \u65E0\u6CD5\u89E3\u6790 serverUrl:",String(t)),null}}function le(e,n){const r=C(e);if(!r){s.warn("[TunnelClient] serverUrl \u65E0\u6548\uFF0C\u65E0\u6CD5\u542F\u52A8\u96A7\u9053:",e);return}const t=p.get(r);t&&(t.shuttingDown=!0,S(t),p.delete(r));const o={serverUrl:r,instanceId:n,streams:new Map,pendingReqChunks:new Map,shuttingDown:!1};p.set(r,o),B(o)}function de(){for(const e of p.values())e.shuttingDown=!0,S(e);p.clear()}function fe(e){const n=C(e);if(!n)return;const r=p.get(n);r&&(r.shuttingDown=!0,S(r),p.delete(n),s.info(`[TunnelClient] tunnel stopped: ${n}`))}function ae(e){const n=new Set(e.map(r=>C(r)).filter(r=>!!r));for(const[r,t]of p.entries())n.has(r)||(s.info(`[TunnelClient] serverUrl \u5DF2\u4E0D\u5728\u914D\u7F6E\u4E2D\uFF0C\u505C\u6B62\u65E7\u96A7\u9053: ${r}`),t.shuttingDown=!0,S(t),p.delete(r))}function S(e){e.timer&&(clearInterval(e.timer),e.timer=void 0),e.reconnectTimer&&(clearTimeout(e.reconnectTimer),e.reconnectTimer=void 0);for(const[n,r]of e.streams.entries()){try{r.ws.close(1e3,"tunnel client stopped")}catch{}e.streams.delete(n)}if(e.ws){e.ws.removeAllListeners();try{e.ws.close(1e3,"tunnel client stopped")}catch{}e.ws=void 0}}function B(e){if(e.shuttingDown)return;const n=e.instanceId;if(!n)return;const r=F(e);if(!r){b(e);return}s.info("[TunnelClient] \u8FDE\u63A5\u96A7\u9053\u670D\u52A1\u7AEF:",r.url);const t=new g(r.url,{handshakeTimeout:15e3,headers:{"X-Runtime-Token":r.token}});e.ws=t,t.on("open",()=>{s.info("[TunnelClient] \u96A7\u9053\u8FDE\u63A5\u5DF2\u5EFA\u7ACB: server=",e.serverUrl,"instanceId=",n),e.reconnectTimer&&clearTimeout(e.reconnectTimer),e.reconnectTimer=void 0,e.reconnectAttempts=0,e.timer=setInterval(()=>{t.readyState===g.OPEN&&t.send(JSON.stringify({t:"ping"}))},$)}),t.on("unexpected-response",(o,u)=>{s.error("[TunnelClient] \u670D\u52A1\u7AEF\u62D2\u7EDD\u8FDE\u63A5\uFF08\u975E 101 \u54CD\u5E94\uFF09: server=",e.serverUrl,"statusCode=",u.statusCode,"statusMessage=",u.statusMessage)}),t.on("upgrade",o=>{s.debug("[TunnelClient] \u6536\u5230\u5347\u7EA7\u54CD\u5E94: statusCode=",o.statusCode)}),t.on("message",o=>{let u;typeof o=="string"?u=o:Buffer.isBuffer(o)?u=o.toString("utf-8"):u=Buffer.from(o).toString("utf-8");try{const l=JSON.parse(u);M(e,l)}catch(l){s.warn("[TunnelClient] \u89E3\u6790\u96A7\u9053\u6D88\u606F\u5931\u8D25:",String(l))}}),t.on("close",(o,u)=>{if(s.warn("[TunnelClient] \u96A7\u9053\u8FDE\u63A5\u5173\u95ED: server=",e.serverUrl,"code=",o,"reason=",String(u)),k(e),o===X){s.warn("[TunnelClient] \u9274\u6743\u5931\u8D25\uFF084001\uFF09\uFF0C\u505C\u6B62\u81EA\u52A8\u91CD\u8FDE\uFF1B\u5F85\u91CD\u65B0\u6CE8\u518C\u540E\u518D\u542F\u52A8\u96A7\u9053: server=",e.serverUrl);return}b(e)}),t.on("error",o=>{s.warn("[TunnelClient] \u96A7\u9053\u8FDE\u63A5\u9519\u8BEF: server=",e.serverUrl,"err=",o.message,"code=",o.code),k(e),b(e)})}function k(e){e.timer&&(clearInterval(e.timer),e.timer=void 0);for(const[n,r]of e.streams.entries()){try{r.ws.close(1011,"tunnel disconnected")}catch{}e.streams.delete(n)}for(const n of Array.from(e.pendingReqChunks.keys()))E(e,n);e.ws=void 0}function b(e){if(e.shuttingDown||e.reconnectTimer)return;const n=(e.reconnectAttempts??0)+1;e.reconnectAttempts=n;const r=Math.min(G*Math.pow(2,n-1),H),t=1-A+Math.random()*A*2,o=Math.round(r*t);s.debug("[TunnelClient] \u8BA1\u5212\u91CD\u8FDE: server=",e.serverUrl,"attempt=",n,"delayMs=",o),e.reconnectTimer=setTimeout(()=>{e.reconnectTimer=void 0,B(e)},o)}function M(e,n){switch(n.t){case"req":n.chunked?V(e,n):U(e,n);break;case"reqchunk":Q(e,n);break;case"reqend":Z(e,n);break;case"wsopen":ee(e,n);break;case"wsmsg":ne(e,n);break;case"wsclose":re(e,n);break;case"ping":d(e,{t:"pong"});break;case"pong":break;default:s.debug("[TunnelClient] \u672A\u77E5\u6D88\u606F\u7C7B\u578B: t=",n.t)}}function V(e,n){E(e,n.id);const r=setTimeout(()=>{s.warn("[TunnelClient] \u8BF7\u6C42\u4F53\u62C6\u5E27\u8D85\u65F6\uFF0C\u4E22\u5F03\u534A\u5305:",n.id),e.pendingReqChunks.delete(n.id)},z);r.unref?.(),e.pendingReqChunks.set(n.id,{method:n.method||"GET",path:n.path||"/",headers:n.headers,parts:[],timer:r})}function Q(e,n){const r=e.pendingReqChunks.get(n.id);if(!r){s.debug("[TunnelClient] \u6536\u5230\u65E0\u4E3B reqchunk\uFF0C\u5DF2\u5FFD\u7565:",n.id);return}n.d&&r.parts.push(Buffer.from(n.d,"base64"))}async function Z(e,n){const r=e.pendingReqChunks.get(n.id);if(!r){s.debug("[TunnelClient] \u6536\u5230\u65E0\u4E3B reqend\uFF0C\u5DF2\u5FFD\u7565:",n.id);return}E(e,n.id);const t=Buffer.concat(r.parts);await U(e,{t:"req",id:n.id,method:r.method,path:r.path,headers:r.headers,body:t.length>0?t.toString("base64"):null})}function E(e,n){const r=e.pendingReqChunks.get(n);r&&(clearTimeout(r.timer),e.pendingReqChunks.delete(n))}async function U(e,n){const r=n.id,t=n.method||"GET",o=n.path||"/",u=`http://127.0.0.1:${R()}${o}`;try{const l=n.body?Buffer.from(n.body,"base64"):void 0,i={};if(n.headers)for(const[a,c]of Object.entries(n.headers))c&&c.length>0&&(i[a]=c[0]);i["x-bridge-tunnel-source"]=e.serverUrl;const f=await O.request({url:u,method:t,headers:i,data:l,timeout:0,responseType:"stream",validateStatus:()=>!0}),h={};for(const[a,c]of Object.entries(f.headers))c!==void 0&&(h[a]=Array.isArray(c)?c:[String(c)]);const v=f.data,I=String(h["content-type"]?.[0]||"").toLowerCase().includes("text/event-stream"),m=[];let w=0,T=!1,y=!1;try{if(v)for await(const a of v){const c=Buffer.isBuffer(a)?a:Buffer.from(a);if(c.length!==0){if(T){if(!_(e,r,c))return}else if(I){if(T=!0,y=!0,!d(e,{t:"res",id:r,status:f.status,headers:h,body:null,chunked:!0})||!_(e,r,c))return}else if(m.push(c),w+=c.length,w>Y){if(T=!0,y=!0,!d(e,{t:"res",id:r,status:f.status,headers:h,body:null,chunked:!0}))return;for(const D of m)if(!_(e,r,D))return;m.length=0,w=0}}}}catch(a){const c=a instanceof Error?a.message:String(a);s.warn("[TunnelClient] \u672C\u5730\u6D41\u8BFB\u53D6\u4E2D\u65AD:",t,o,"->",c),y?d(e,{t:"resend",id:r,error:c}):d(e,{t:"res",id:r,status:502,headers:{"content-type":["text/plain; charset=utf-8"]},body:Buffer.from(c).toString("base64")});return}if(T){d(e,{t:"resend",id:r});return}const N=w>0?Buffer.concat(m).toString("base64"):"";d(e,{t:"res",id:r,status:f.status,headers:h,body:N||null})}catch(l){const i=l instanceof x?l.message:String(l);s.warn("[TunnelClient] \u672C\u5730\u8F6C\u53D1\u5931\u8D25:",t,o,"->",i),d(e,{t:"res",id:r,status:502,headers:{"content-type":["text/plain; charset=utf-8"]},body:Buffer.from(i).toString("base64")})}}function _(e,n,r){let t=0;for(;t<r.length;){const o=r.subarray(t,t+j);if(!d(e,{t:"reschunk",id:n,d:o.toString("base64")}))return!1;t+=o.length}return!0}function ee(e,n){const r=n.id,t=n.path||"/",o=`ws://127.0.0.1:${R()}${t}`;let u;try{u=new g(o,{handshakeTimeout:1e4})}catch(i){s.warn("[TunnelClient] \u672C\u5730 WS \u8FDE\u63A5\u5931\u8D25:",String(i)),d(e,{t:"wsclose",id:r,code:1011,reason:"local ws connect failed"});return}const l={ws:u,pending:[]};e.streams.set(r,l),u.on("open",()=>{for(const i of l.pending)q(l,i.data,i.bin);l.pending=[],d(e,{t:"wsopened",id:r})}),u.on("message",(i,f)=>{const h=Buffer.isBuffer(i)?i:Buffer.from(i);f&&e.ws&&e.ws.bufferedAmount>K||d(e,{t:"wsmsg",id:r,bin:f,d:h.toString("base64")})}),u.on("close",(i,f)=>{e.streams.delete(r),d(e,{t:"wsclose",id:r,code:i,reason:String(f)})}),u.on("error",i=>{s.warn("[TunnelClient] \u672C\u5730 WS \u9519\u8BEF: id=",r,"err=",i.message),e.streams.delete(r);try{u.close(1011,i.message)}catch{}d(e,{t:"wsclose",id:r,code:1011,reason:i.message})})}function q(e,n,r){r?e.ws.send(n):e.ws.send(n.toString("utf-8"))}function ne(e,n){const r=e.streams.get(n.id);if(!r)return;const t=n.d?Buffer.from(n.d,"base64"):Buffer.alloc(0);if(r.ws.readyState!==g.OPEN){r.pending.length>=J&&(r.pending.shift(),s.warn("[TunnelClient] \u672C\u5730 WS \u672A\u5C31\u7EEA\uFF0C\u6392\u961F\u6D88\u606F\u8D85\u4E0A\u9650\uFF0C\u4E22\u5F03\u6700\u65E7\u4E00\u6761: id=",n.id)),r.pending.push({data:t,bin:!!n.bin});return}q(r,t,!!n.bin)}function re(e,n){const r=e.streams.get(n.id);if(r){e.streams.delete(n.id);try{r.ws.close(n.code??1e3,n.reason||"")}catch{}}}function d(e,n){if(!e.ws||e.ws.readyState!==g.OPEN)return!1;try{return e.ws.send(JSON.stringify(n)),!0}catch(r){return s.warn("[TunnelClient] sendJson \u5931\u8D25:",String(r)),!1}}const he={createState(e="http://server.test",n="test-instance"){return{serverUrl:e,instanceId:n,streams:new Map,pendingReqChunks:new Map,shuttingDown:!1}},dispatch(e,n){M(e,n)},pendingChunkIds(e){return Array.from(e.pendingReqChunks.keys())},cleanup(e){k(e)}};export{he as __tunnelClientTestHooks,le as startTunnelClient,de as stopTunnelClient,fe as stopTunnelClientForServer,ae as stopTunnelClientsExcept};
2
2
 
@@ -1,4 +1,4 @@
1
- import{createHash as $}from"node:crypto";import{promises as r}from"node:fs";import s from"node:path";import G from"axios";import*as X from"diff";import{resolveSchedulerBaseUrl as J,getRuntimeHomeDir as C}from"../config.js";import{createLogger as V}from"../utils/logger.js";import{getChangeCacheRetentionDays as D,setChangeCacheRetentionDays as Z}from"./change-cache-retention.js";import{getRuntimeAccessToken as H}from"./runtime-binding.js";const h=V("workspace-task-change-watcher"),q=10*1024*1024,Q=".agentswork/change-cache",m="change-cache",Y=60*60*1e3,ee=24*60*60*1e3;let l=null,T=!1,k="",u=null;const g=new Map,d=new Map;let b=!1;function de(e){l&&l.taskNodeId!==e.taskNodeId&&K().catch(()=>{}),l=e,h.info(`Task context set: agent=${e.agentId}, taskInstance=${e.taskInstanceId}, node=${e.taskNodeId}`)}function pe(){l&&(h.info(`Task context cleared: agent=${l.agentId}`),K().catch(()=>{})),l=null}function ge(){return l}function we(){return l!==null}async function Ce(e){const t=s.resolve(e.trim());try{if(!(await r.stat(t)).isDirectory())throw new Error(`Not a directory: ${t}`)}catch{throw new Error(`Workspace path does not exist or inaccessible: ${t}`)}if(u){try{u.close()}catch{}u=null}k=t,T=!0,h.info(`Workspace path set (fs.watch disabled, using tool interception only): ${k}`)}async function me(){if(T=!1,u){try{u.close()}catch{}u=null}g.clear(),d.clear(),k="",h.info("Workspace watch stopped")}function L(e,t,a){return $("sha256").update(`${e||"no-task"}:${t}:${a}`,"utf-8").digest("hex").slice(0,32)}function F(e){return $("sha256").update(s.resolve(e),"utf-8").digest("hex").slice(0,16)}function y(e){return s.join(C(),".aws-bridge",m,F(e))}function te(e){return s.join(C(),m,F(e))}function N(e){return s.join(e,Q)}const W=new Set;async function j(e){if(!W.has(e)){W.add(e);try{const t=N(e),a=y(e);await r.access(t),await r.mkdir(s.dirname(a),{recursive:!0}),await r.rename(t,a),h.info(`Migrated legacy change-cache: ${t} -> ${a}`)}catch{}}}async function v(e,t,a){if(a===null)return;await j(e);const o=y(e);await r.mkdir(o,{recursive:!0});const n=s.join(o,`${t}.before`);await r.writeFile(n,a,"utf-8"),U(),h.debug(`Saved beforeContent cache: ${n}`)}async function _(e,t,a,o){const n=X.createPatch("file",a,o);await j(e);const i=y(e);await r.mkdir(i,{recursive:!0});const c=s.join(i,`${t}.diff`);await r.writeFile(c,n,"utf-8"),U(),h.debug(`Saved diff cache: ${c}`)}async function M(e,t){const a=[s.join(y(e),t),s.join(te(e),t),s.join(N(e),t)];for(const o of a)try{return await r.readFile(o,"utf-8")}catch{}return null}async function ke(e,t){return M(e,`${t}.before`)}async function ye(e,t){return M(e,`${t}.diff`)}let x=0,A=null,P=Promise.resolve();function R(){return P=P.then(()=>ne()),P}async function ne(){const e=D();if(!(e===null||e<=0))try{const t=[s.join(C(),".aws-bridge",m),s.join(C(),m)],a=Date.now()-e*24*60*60*1e3;let o=0;for(const n of t){let i=[];try{i=(await r.readdir(n)).filter(c=>!c.startsWith("."))}catch{continue}for(const c of i){const f=s.join(n,c);try{const p=await r.readdir(f);let w=!0;for(const E of p){if(!E.endsWith(".diff")&&!E.endsWith(".before")){w=!1;continue}const S=s.join(f,E);try{(await r.stat(S)).mtimeMs<a?(await r.unlink(S),o++):w=!1}catch{w=!1}}w&&await r.rmdir(f).catch(()=>{})}catch{}}}o>0&&h.info(`Change-cache TTL sweep: removed ${o} expired file(s), retentionDays=${e}`)}catch(t){h.warn(`Change-cache TTL sweep failed: ${t.message}`)}}function U(){const e=Date.now();e-x>=Y&&(x=e,R().catch(()=>{})),A||(A=setInterval(()=>{R().catch(()=>{})},ee),A.unref?.())}async function B(e){try{return(await r.stat(e)).size>q?null:await r.readFile(e,"utf-8")}catch{return null}}function I(e){return e===null?null:$("sha256").update(e,"utf-8").digest("hex").slice(0,16)}async function ae(e){const t=g.get(e);if(!t)return;g.delete(e),t.timer&&clearTimeout(t.timer);const a=l;if(!a){if(t.operation==="DELETE")d.delete(e);else{const p=await B(e);p!==null&&d.set(e,p)}return}const o=s.relative(a.workspacePath,e).replace(/\\/g,"/");if(o.startsWith(".."))return;let n=t.beforeContent,i=null;t.operation==="DELETE"?(n=n??d.get(e)??null,i=null):(i=await B(e),t.operation==="UPDATE"&&n===null&&(n=d.get(e)??null)),t.operation==="DELETE"?d.delete(e):d.set(e,i);const c=new Date().toISOString(),f=L(a.taskInstanceId,o,c);t.operation==="UPDATE"&&n!==null&&i!==null?await _(a.workspacePath,f,n,i):t.operation==="DELETE"&&await v(a.workspacePath,f,n),await O(a,{workspacePath:a.workspacePath,filePath:o,operation:t.operation,beforeContent:n,afterContent:i,beforeHash:I(n),afterHash:I(i),timestamp:c,reason:z(t.operation,n,i)},f)}async function O(e,t,a){try{const o=J().url,n=H(o)||H();if(!n){h.warn("Cannot send change record: no auth token");return}const i=JSON.stringify({toolRecorded:!0,cacheKey:a,timestamp:t.timestamp}),f=(await G.post(`${o}/api/runtime/callback/task-file-changed`,{agentId:e.agentId,taskInstanceId:e.taskInstanceId,taskNodeId:e.taskNodeId,tenantId:e.tenantId,operation:t.operation,targetType:"FILE",targetPath:t.filePath,beforeHash:t.beforeHash,afterHash:t.afterHash,metadata:i,reason:t.reason,workspacePath:e.workspacePath},{headers:{"X-Runtime-Token":n},timeout:1e4})).data;if(f?.ignored===!0?h.warn(`Change record ignored by server: ${t.filePath} (${t.operation}), reason: ${String(f.reason??"unknown")}`):h.info(`Change record sent: ${t.filePath} (${t.operation})`),f&&f.changeCacheRetentionDays!==void 0){const p=D();Z(f.changeCacheRetentionDays),p!==D()&&(x=0,R().catch(()=>{}))}}catch(o){h.warn(`Failed to send change record: ${o.message}`)}}async function K(){if(b)return;b=!0;const e=Array.from(g.keys());for(const t of e)try{await ae(t)}catch{}b=!1}function Ie(){return g.size}function Ee(){return T&&u!==null}function $e(){return k}function z(e,t,a){const o=t?t.split(`
1
+ import{createHash as E}from"node:crypto";import{promises as r}from"node:fs";import c from"node:path";import X from"axios";import*as J from"diff";import{resolveSchedulerBaseUrl as V,getRuntimeHomeDir as C}from"../config.js";import{createLogger as Z}from"../utils/logger.js";import{getChangeCacheRetentionDays as $,setChangeCacheRetentionDays as q}from"./change-cache-retention.js";import{getSnapshotRetentionDays as T,setSnapshotRetentionDays as Q}from"./snapshot-retention.js";import{sweepExpiredSnapshots as Y}from"./fileSnapshotStore.js";import{getRuntimeAccessToken as L}from"./runtime-binding.js";const h=Z("workspace-task-change-watcher"),ee=10*1024*1024,te=".agentswork/change-cache",m="change-cache",ne=60*60*1e3,ae=24*60*60*1e3;let l=null,b=!1,y="",p=null;const g=new Map,d=new Map;let R=!1;function me(e){l&&l.taskNodeId!==e.taskNodeId&&z().catch(()=>{}),l=e,h.info(`Task context set: agent=${e.agentId}, taskInstance=${e.taskInstanceId}, node=${e.taskNodeId}`)}function ye(){l&&(h.info(`Task context cleared: agent=${l.agentId}`),z().catch(()=>{})),l=null}function ke(){return l}function De(){return l!==null}async function Ie(e){const t=c.resolve(e.trim());try{if(!(await r.stat(t)).isDirectory())throw new Error(`Not a directory: ${t}`)}catch{throw new Error(`Workspace path does not exist or inaccessible: ${t}`)}if(p){try{p.close()}catch{}p=null}y=t,b=!0,h.info(`Workspace path set (fs.watch disabled, using tool interception only): ${y}`)}async function Ee(){if(b=!1,p){try{p.close()}catch{}p=null}g.clear(),d.clear(),y="",h.info("Workspace watch stopped")}function F(e,t,a){return E("sha256").update(`${e||"no-task"}:${t}:${a}`,"utf-8").digest("hex").slice(0,32)}function N(e){return E("sha256").update(c.resolve(e),"utf-8").digest("hex").slice(0,16)}function k(e){return c.join(C(),".aws-bridge",m,N(e))}function oe(e){return c.join(C(),m,N(e))}function W(e){return c.join(e,te)}const j=new Set;async function _(e){if(!j.has(e)){j.add(e);try{const t=W(e),a=k(e);await r.access(t),await r.mkdir(c.dirname(a),{recursive:!0}),await r.rename(t,a),h.info(`Migrated legacy change-cache: ${t} -> ${a}`)}catch{}}}async function v(e,t,a){if(a===null)return;await _(e);const o=k(e);await r.mkdir(o,{recursive:!0});const n=c.join(o,`${t}.before`);await r.writeFile(n,a,"utf-8"),B(),h.debug(`Saved beforeContent cache: ${n}`)}async function M(e,t,a,o){const n=J.createPatch("file",a,o);await _(e);const i=k(e);await r.mkdir(i,{recursive:!0});const f=c.join(i,`${t}.diff`);await r.writeFile(f,n,"utf-8"),B(),h.debug(`Saved diff cache: ${f}`)}async function U(e,t){const a=[c.join(k(e),t),c.join(oe(e),t),c.join(W(e),t)];for(const o of a)try{return await r.readFile(o,"utf-8")}catch{}return null}async function $e(e,t){return U(e,`${t}.before`)}async function Te(e,t){return U(e,`${t}.diff`)}let x=0,S=null,A=Promise.resolve();function P(){return A=A.then(()=>ie()),A}async function ie(){const e=$();if(!(e===null||e<=0))try{const t=[c.join(C(),".aws-bridge",m),c.join(C(),m)],a=Date.now()-e*24*60*60*1e3;let o=0;for(const n of t){let i=[];try{i=(await r.readdir(n)).filter(f=>!f.startsWith("."))}catch{continue}for(const f of i){const s=c.join(n,f);try{const u=await r.readdir(s);let w=!0;for(const I of u){if(!I.endsWith(".diff")&&!I.endsWith(".before")){w=!1;continue}const H=c.join(s,I);try{(await r.stat(H)).mtimeMs<a?(await r.unlink(H),o++):w=!1}catch{w=!1}}w&&await r.rmdir(s).catch(()=>{})}catch{}}}o>0&&h.info(`Change-cache TTL sweep: removed ${o} expired file(s), retentionDays=${e}`)}catch(t){h.warn(`Change-cache TTL sweep failed: ${t.message}`)}}function B(){const e=Date.now();e-x>=ne&&(x=e,P().catch(()=>{})),S||(S=setInterval(()=>{P().catch(()=>{})},ae),S.unref?.())}async function O(e){try{return(await r.stat(e)).size>ee?null:await r.readFile(e,"utf-8")}catch{return null}}function D(e){return e===null?null:E("sha256").update(e,"utf-8").digest("hex").slice(0,16)}async function se(e){const t=g.get(e);if(!t)return;g.delete(e),t.timer&&clearTimeout(t.timer);const a=l;if(!a){if(t.operation==="DELETE")d.delete(e);else{const u=await O(e);u!==null&&d.set(e,u)}return}const o=c.relative(a.workspacePath,e).replace(/\\/g,"/");if(o.startsWith(".."))return;let n=t.beforeContent,i=null;t.operation==="DELETE"?(n=n??d.get(e)??null,i=null):(i=await O(e),t.operation==="UPDATE"&&n===null&&(n=d.get(e)??null)),t.operation==="DELETE"?d.delete(e):d.set(e,i);const f=new Date().toISOString(),s=F(a.taskInstanceId,o,f);t.operation==="UPDATE"&&n!==null&&i!==null?await M(a.workspacePath,s,n,i):t.operation==="DELETE"&&await v(a.workspacePath,s,n),await K(a,{workspacePath:a.workspacePath,filePath:o,operation:t.operation,beforeContent:n,afterContent:i,beforeHash:D(n),afterHash:D(i),timestamp:f,reason:G(t.operation,n,i)},s)}async function K(e,t,a){try{const o=V().url,n=L(o)||L();if(!n){h.warn("Cannot send change record: no auth token");return}const i=JSON.stringify({toolRecorded:!0,cacheKey:a,timestamp:t.timestamp}),s=(await X.post(`${o}/api/runtime/callback/task-file-changed`,{agentId:e.agentId,taskInstanceId:e.taskInstanceId,taskNodeId:e.taskNodeId,tenantId:e.tenantId,operation:t.operation,targetType:"FILE",targetPath:t.filePath,beforeHash:t.beforeHash,afterHash:t.afterHash,metadata:i,reason:t.reason,workspacePath:e.workspacePath},{headers:{"X-Runtime-Token":n},timeout:1e4})).data;if(s?.ignored===!0?h.warn(`Change record ignored by server: ${t.filePath} (${t.operation}), reason: ${String(s.reason??"unknown")}`):h.info(`Change record sent: ${t.filePath} (${t.operation})`),s&&s.changeCacheRetentionDays!==void 0){const u=$();q(s.changeCacheRetentionDays),u!==$()&&(x=0,P().catch(()=>{}))}if(s&&s.snapshotRetentionDays!==void 0){const u=T();Q(s.snapshotRetentionDays),u!==T()&&Y(T()).catch(()=>{})}}catch(o){h.warn(`Failed to send change record: ${o.message}`)}}async function z(){if(R)return;R=!0;const e=Array.from(g.keys());for(const t of e)try{await se(t)}catch{}R=!1}function be(){return g.size}function Re(){return b&&p!==null}function ve(){return y}function G(e,t,a){const o=t?t.split(`
2
2
  `).length:0,n=a?a.split(`
3
- `).length:0;if(e==="CREATE")return`\u521B\u5EFA\u6587\u4EF6\uFF0C\u5171 ${n} \u884C`;if(e==="DELETE")return`\u5220\u9664\u6587\u4EF6\uFF0C\u539F ${o} \u884C`;if(o===n)return`\u4FEE\u6539\u6587\u4EF6\uFF08\u884C\u6570\u4E0D\u53D8\uFF0C${n} \u884C\uFF09`;const i=n-o;return`\u4FEE\u6539\u6587\u4EF6\uFF1A${o} \u884C -> ${n} \u884C\uFF08${i>0?"+":""}${i} \u884C\uFF09`}async function De(e){const t=l,a=e.agentId||t?.agentId,o=e.workspacePath||t?.workspacePath;if(!a||!o){h.warn(`Agent file change skipped (no identity): ${e.filePath} (${e.operation})`);return}h.info(`Recording agent file change: ${e.filePath} (${e.operation}), taskNode=${t?.taskNodeId??"(no task)"}`);const n=t??{agentId:a,taskInstanceId:"",taskNodeId:"",tenantId:"",workspacePath:o,projectId:""},i=new Date().toISOString(),c=L(n.taskInstanceId,e.filePath,i);e.operation==="UPDATE"&&e.beforeContent!==null&&e.afterContent!==null?(await _(n.workspacePath,c,e.beforeContent,e.afterContent),await v(n.workspacePath,c,e.beforeContent)):e.operation==="DELETE"&&await v(n.workspacePath,c,e.beforeContent);const f=e.reason?.trim()||z(e.operation,e.beforeContent,e.afterContent);await O(n,{workspacePath:n.workspacePath,filePath:e.filePath,operation:e.operation,beforeContent:e.beforeContent,afterContent:e.afterContent,beforeHash:I(e.beforeContent),afterHash:I(e.afterContent),timestamp:i,reason:f},c)}export{pe as clearTaskContext,Ie as getPendingChangeCountForTest,ge as getTaskContext,$e as getWorkspaceRoot,we as hasActiveTask,Ee as isWatching,ke as readBeforeContentFromCache,ye as readDiffFromCache,De as recordAgentFileChange,de as setTaskContext,Ce as startWatching,me as stopWatching,R as sweepExpiredChangeCache};
3
+ `).length:0;if(e==="CREATE")return`\u521B\u5EFA\u6587\u4EF6\uFF0C\u5171 ${n} \u884C`;if(e==="DELETE")return`\u5220\u9664\u6587\u4EF6\uFF0C\u539F ${o} \u884C`;if(o===n)return`\u4FEE\u6539\u6587\u4EF6\uFF08\u884C\u6570\u4E0D\u53D8\uFF0C${n} \u884C\uFF09`;const i=n-o;return`\u4FEE\u6539\u6587\u4EF6\uFF1A${o} \u884C -> ${n} \u884C\uFF08${i>0?"+":""}${i} \u884C\uFF09`}async function xe(e){const t=l,a=e.agentId||t?.agentId,o=e.workspacePath||t?.workspacePath;if(!a||!o){h.warn(`Agent file change skipped (no identity): ${e.filePath} (${e.operation})`);return}h.info(`Recording agent file change: ${e.filePath} (${e.operation}), taskNode=${t?.taskNodeId??"(no task)"}`);const n=t??{agentId:a,taskInstanceId:"",taskNodeId:"",tenantId:"",workspacePath:o,projectId:""},i=new Date().toISOString(),f=F(n.taskInstanceId,e.filePath,i);e.operation==="UPDATE"&&e.beforeContent!==null&&e.afterContent!==null?(await M(n.workspacePath,f,e.beforeContent,e.afterContent),await v(n.workspacePath,f,e.beforeContent)):e.operation==="DELETE"&&await v(n.workspacePath,f,e.beforeContent);const s=e.reason?.trim()||G(e.operation,e.beforeContent,e.afterContent);await K(n,{workspacePath:n.workspacePath,filePath:e.filePath,operation:e.operation,beforeContent:e.beforeContent,afterContent:e.afterContent,beforeHash:D(e.beforeContent),afterHash:D(e.afterContent),timestamp:i,reason:s},f)}export{ye as clearTaskContext,be as getPendingChangeCountForTest,ke as getTaskContext,ve as getWorkspaceRoot,De as hasActiveTask,Re as isWatching,$e as readBeforeContentFromCache,Te as readDiffFromCache,xe as recordAgentFileChange,me as setTaskContext,Ie as startWatching,Ee as stopWatching,P as sweepExpiredChangeCache};
4
4
 
@@ -0,0 +1,2 @@
1
+
2
+ export declare function isLLMSummaryRequest(messages: ReadonlyArray<Record<string, unknown>>): boolean;
@@ -0,0 +1,2 @@
1
+ function r(t){if(t.length===0)return!1;const n=t[t.length-1];return n.role==="user"&&String(n.content||"").includes("## Goal")}export{r as isLLMSummaryRequest};
2
+
@@ -0,0 +1,26 @@
1
+
2
+
3
+ export type CacheControlMode = "off" | "message" | "content-block";
4
+
5
+ export interface CacheControlMessage {
6
+ role: string;
7
+ content?: unknown;
8
+ [key: string]: unknown;
9
+ }
10
+
11
+ export declare function asCacheControlMessages(messages: readonly unknown[]): CacheControlMessage[];
12
+
13
+ export interface CacheControlInjection {
14
+
15
+ index: number;
16
+
17
+ message: CacheControlMessage;
18
+ }
19
+
20
+ export declare function findPreviousNonSystemMessageIndex(messages: readonly CacheControlMessage[], startIndex: number): number | undefined;
21
+
22
+ export declare function clearNonSystemMessageCacheControl(messages: CacheControlMessage[]): void;
23
+
24
+ export declare function injectCacheControl(messages: CacheControlMessage[], mode: CacheControlMode | undefined, options?: {
25
+ skipCacheWrite?: boolean;
26
+ }): CacheControlInjection | undefined;
@@ -0,0 +1,2 @@
1
+ function s(e){return e}function l(e,n){for(let t=Math.min(n,e.length-1);t>=0;t-=1)if(e[t]?.role!=="system")return t}function h(e){for(let n=0;n<e.length;n+=1){const t=e[n];if(t.role==="system"||!("cache_control"in t))continue;const{cache_control:a,...r}=t;e[n]=r}}function x(e,n,t={}){if(!n||n==="off"||e.length===0)return;h(e);const a=l(e,e.length-1);if(a===void 0)return;const r=t.skipCacheWrite===!0?l(e,a-1):a;if(r===void 0)return;const c=e[r],i={type:"ephemeral"};if(n==="message")return{index:r,message:{...c,cache_control:{...i}}};const u=c.content;if(typeof u=="string")return{index:r,message:{...c,content:[{type:"text",text:u,cache_control:{...i}}]}};if(Array.isArray(u)){const o=u.map(d=>({...d}));if(o.length===0)return o.push({type:"text",text:"",cache_control:{...i}}),{index:r,message:{...c,content:o}};const f=o.length-1;return o[f]={...o[f],cache_control:{...i}},{index:r,message:{...c,content:o}}}return{index:r,message:{...c,cache_control:{...i}}}}export{s as asCacheControlMessages,h as clearNonSystemMessageCacheControl,l as findPreviousNonSystemMessageIndex,x as injectCacheControl};
2
+
@@ -15,6 +15,16 @@ export declare const RUNTIME_RESOURCES_PREFIX = "ACode runtime resources:";
15
15
  export declare const RUNTIME_INSTRUCTIONS_PREFIX = "[Runtime Instructions]";
16
16
 
17
17
  export declare const COMPACTION_LLM_PROMPT_TEMPLATE: string;
18
+
19
+ export { ESTIMATION_CJK_CHARS_PER_TOKEN } from "./token-estimator.js";
20
+
21
+ export { ESTIMATION_ASCII_CHARS_PER_TOKEN } from "./token-estimator.js";
22
+
23
+ export { ESTIMATION_SAFE_PADDING } from "./token-estimator.js";
24
+
25
+ export { ESTIMATION_IMAGE_TOKENS } from "./token-estimator.js";
26
+
27
+ export { ESTIMATION_TOOL_OVERHEAD } from "./token-estimator.js";
18
28
  interface ChatMessageLike {
19
29
  role: "system" | "user" | "assistant" | "tool";
20
30
  content: string | unknown[] | null;
@@ -56,11 +66,15 @@ export interface CompactionResult {
56
66
 
57
67
  downsampleCount?: number;
58
68
 
69
+ droppedMessages?: number;
70
+
59
71
  noSummary?: boolean;
60
72
 
61
73
  reachedTarget?: boolean;
62
74
 
63
75
  retryAttempts?: number;
76
+
77
+ contextTokensAfter?: number;
64
78
  }
65
79
 
66
80
  export type CompactionLevel = 1 | 2 | 3 | 4;
@@ -136,11 +150,24 @@ export interface CompactionPhaseEvent {
136
150
  reason?: string;
137
151
  }
138
152
 
153
+ export interface SummaryRequestMessage {
154
+ role: "system" | "user" | "assistant" | "tool";
155
+ content: string | unknown[] | null;
156
+ tool_call_id?: string;
157
+ tool_calls?: unknown[];
158
+ }
159
+
139
160
  export type LLMSummarizer = (input: {
140
- conversationText: string;
161
+
162
+ messages: readonly SummaryRequestMessage[];
163
+
164
+ prefixMessages?: readonly SummaryRequestMessage[];
165
+
141
166
  messageCount: number;
142
167
  contextLimit: number;
143
168
 
169
+ reasoningText?: string;
170
+
144
171
  previousSummary?: string;
145
172
 
146
173
  anchors?: FidelityAnchors;
@@ -189,14 +216,14 @@ export declare function lookupModelLimits(modelName: string, configOverrides?: {
189
216
  maxOutput: number;
190
217
  };
191
218
 
192
- export declare function estimateTextTokens(text: string): number;
193
-
194
- export declare function estimateTokens(text: string): number;
219
+ export { estimateTextTokens, estimateTokens } from "./token-estimator.js";
195
220
 
196
221
  export declare function conversationSizeBytes(messages: readonly ChatMessageLike[]): number;
197
222
 
198
223
  export declare function estimateConversationTokens(messages: readonly ChatMessageLike[]): number;
199
224
 
225
+ export declare function estimateContextTokens(messages: readonly ChatMessageLike[], toolsTokens?: number): number;
226
+
200
227
  export declare function conversationSizeKB(messages: readonly ChatMessageLike[]): number;
201
228
 
202
229
  export declare function isOverflow(messages: readonly ChatMessageLike[], contextLimit: number, _maxOutput?: number): boolean;
@@ -231,6 +258,8 @@ export declare function summarizeConversation(messages: ChatMessageLike[], optio
231
258
 
232
259
  includeReasoning?: boolean;
233
260
 
261
+ prefixMessages?: readonly SummaryRequestMessage[];
262
+
234
263
  stats?: {
235
264
  downsampleCount: number;
236
265
  droppedMessages: number;
@@ -255,6 +284,7 @@ export declare function performCompaction(messages: ChatMessageLike[], contextLi
255
284
 
256
285
  retryPolicy?: SummaryRetryPolicy;
257
286
 
287
+ prefixMessages?: readonly SummaryRequestMessage[];
288
+
258
289
  allowRuleFallback?: boolean;
259
290
  }): Promise<CompactionResult>;
260
- export {};