@roll-agent/runtime 0.7.0 → 0.8.0

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 (73) hide show
  1. package/dist/bash/classifier/compound.d.ts +2 -1
  2. package/dist/bash/classifier/compound.js +1 -1
  3. package/dist/bash/classifier/flag-audit.js +1 -1
  4. package/dist/bash/classifier/git-audit.d.ts +9 -1
  5. package/dist/bash/classifier/git-audit.js +1 -1
  6. package/dist/bash/classifier/index.d.ts +0 -1
  7. package/dist/bash/classifier/index.js +1 -1
  8. package/dist/bash/classifier/path-audit.d.ts +1 -1
  9. package/dist/bash/classifier/path-audit.js +1 -1
  10. package/dist/bash/classifier/safe-list.d.ts +6 -0
  11. package/dist/bash/classifier/safe-list.js +1 -1
  12. package/dist/bash/clean-env.d.ts +2 -0
  13. package/dist/bash/clean-env.js +1 -1
  14. package/dist/bash/format-result.d.ts +1 -1
  15. package/dist/bash/format-result.js +1 -1
  16. package/dist/bash/profile.js +1 -1
  17. package/dist/bash/session/session-manager.d.ts +1 -0
  18. package/dist/bash/session/session-manager.js +1 -1
  19. package/dist/bash/workdir.js +1 -1
  20. package/dist/engine/agent-session.d.ts +88 -3
  21. package/dist/engine/agent-session.js +1 -1
  22. package/dist/engine/cancelled-turn-recovery.d.ts +34 -0
  23. package/dist/engine/cancelled-turn-recovery.js +1 -0
  24. package/dist/engine/capability-manifest.d.ts +160 -0
  25. package/dist/engine/capability-manifest.js +1 -0
  26. package/dist/engine/compaction-checkpoint.d.ts +3827 -0
  27. package/dist/engine/compaction-checkpoint.js +1 -0
  28. package/dist/engine/compaction-semantic-state.d.ts +1741 -0
  29. package/dist/engine/compaction-semantic-state.js +1 -0
  30. package/dist/engine/compactor.d.ts +35 -3
  31. package/dist/engine/compactor.js +1 -1
  32. package/dist/engine/context-window.js +1 -1
  33. package/dist/engine/conversation-engine.d.ts +17 -4
  34. package/dist/engine/conversation-engine.js +1 -1
  35. package/dist/engine/explicit-skill-context.d.ts +103 -0
  36. package/dist/engine/explicit-skill-context.js +1 -0
  37. package/dist/engine/system-prompt.d.ts +4 -0
  38. package/dist/engine/system-prompt.js +1 -1
  39. package/dist/engine/tool-protocol-repair.d.ts +21 -0
  40. package/dist/engine/tool-protocol-repair.js +1 -0
  41. package/dist/engine/vcs-context.d.ts +3 -0
  42. package/dist/engine/vcs-context.js +1 -0
  43. package/dist/index.d.ts +6 -4
  44. package/dist/index.js +1 -1
  45. package/dist/server/protocol.d.ts +31 -0
  46. package/dist/server/protocol.js +1 -1
  47. package/dist/server/runtime-server.d.ts +21 -1
  48. package/dist/server/runtime-server.js +1 -1
  49. package/dist/store/thread-store.d.ts +96 -0
  50. package/dist/store/thread-store.js +1 -1
  51. package/dist/tool-bridge/agent-install-tool.js +1 -1
  52. package/dist/tool-bridge/bash-tool.d.ts +1 -0
  53. package/dist/tool-bridge/bash-tool.js +1 -1
  54. package/dist/tool-bridge/build-tools.d.ts +10 -1
  55. package/dist/tool-bridge/build-tools.js +1 -1
  56. package/dist/tool-bridge/naming.d.ts +10 -2
  57. package/dist/tool-bridge/naming.js +1 -1
  58. package/dist/tool-bridge/normalize-result.d.ts +73 -1
  59. package/dist/tool-bridge/normalize-result.js +1 -1
  60. package/dist/tool-bridge/session-exec-tool.d.ts +3 -2
  61. package/dist/tool-bridge/session-exec-tool.js +1 -1
  62. package/dist/tool-bridge/skill-tool.d.ts +3 -2
  63. package/dist/tool-bridge/skill-tool.js +1 -1
  64. package/dist/tool-bridge/tool-execution-coordinator.d.ts +69 -0
  65. package/dist/tool-bridge/tool-execution-coordinator.js +1 -0
  66. package/dist/tool-bridge/tool-execution-record.d.ts +148 -0
  67. package/dist/tool-bridge/tool-execution-record.js +1 -0
  68. package/dist/tool-bridge/transcript-tool.d.ts +30 -0
  69. package/dist/tool-bridge/transcript-tool.js +1 -0
  70. package/dist/types/cancellation.d.ts +18 -0
  71. package/dist/types/cancellation.js +1 -1
  72. package/dist/types/events.d.ts +20 -0
  73. package/package.json +2 -2
@@ -1,3 +1,4 @@
1
1
  import type { CommandClassification } from "../../types/command-classification.ts";
2
2
  export declare function containsDangerousMetachar(script: string): boolean;
3
- export declare function classifyScript(script: string, platform: NodeJS.Platform): CommandClassification;
3
+ export declare function containsUnquotedGlob(script: string): boolean;
4
+ export declare function classifyScript(script: string, platform: NodeJS.Platform, workdir?: string): CommandClassification;
@@ -1 +1 @@
1
- import{auditFlags as n,FLAG_AUDITORS as o}from"./flag-audit.js";import{auditGit as t}from"./git-audit.js";import{auditPathArgs as r}from"./path-audit.js";import{isDangerous as e}from"./dangerous.js";import{executableLookupKey as s}from"./lookup-key.js";import{isSafeExecutable as u}from"./safe-list.js";import{tokenizeScript as f}from"./tokenize.js";const i=["$","`",">","<","(",")","{","}","\\"];export function containsDangerousMetachar(n){for(const o of i)if(n.includes(o))return!0;return n.replace(/&&/g,"").includes("&")}function a(f,i){const a=f[0];if(void 0===a)return"unknown";if(e(f,i))return"dangerous";const c=s(a,i);return"safe"!==r(c,f)?"unknown":"git"===c?"safe"===t(f)?"known-safe":"unknown":Object.hasOwn(o,c)?"safe"===n(c,f)?"known-safe":"unknown":u(c,i)?"known-safe":"unknown"}export function classifyScript(n,o){if(containsDangerousMetachar(n))return"unknown";const t=f(n);if(null===t)return"unknown";const r=[],e=[];let s=[];for(const n of t)"op"===n.kind?(r.push(s),e.push(n.value),s=[]):s.push(n.value);r.push(s);const u=r[r.length-1];if(void 0!==u&&0===u.length&&";"===e[e.length-1]&&r.pop(),0===r.length||r.some(n=>0===n.length))return"unknown";let i=!0;for(const n of r){const t=a(n,o);if("dangerous"===t)return"dangerous";"known-safe"!==t&&(i=!1)}return i?"known-safe":"unknown"}
1
+ import{auditFlags as n,FLAG_AUDITORS as o}from"./flag-audit.js";import{auditGit as t}from"./git-audit.js";import{auditPathArgs as e}from"./path-audit.js";import{isDangerous as r}from"./dangerous.js";import{executableLookupKey as s}from"./lookup-key.js";import{isSafeExecutable as u,isTrustedExecutable as i}from"./safe-list.js";import{tokenizeScript as f}from"./tokenize.js";const a=["$","`",">","<","(",")","{","}","\\"];export function containsDangerousMetachar(n){for(const o of a)if(n.includes(o))return!0;return n.replace(/&&/g,"").includes("&")}export function containsUnquotedGlob(n){let o;for(const t of n)if(void 0===o)if("'"!==t&&'"'!==t){if("*"===t||"?"===t||"["===t)return!0}else o=t;else t===o&&(o=void 0);return!1}function c(f,a,c){const l=f[0];if(void 0===l)return"unknown";if(r(f,a))return"dangerous";if(l.includes("/")||l.includes("\\"))return"unknown";const p=s(l,a);return i(p,a)?"safe"!==e(p,f,c)?"unknown":"git"===p?"safe"===t(f,c)?"known-safe":"unknown":Object.hasOwn(o,p)?"safe"===n(p,f)?"known-safe":"unknown":u(p,a)?"known-safe":"unknown":"unknown"}export function classifyScript(n,o,t=""){if(containsDangerousMetachar(n)||containsUnquotedGlob(n))return"unknown";const e=f(n);if(null===e)return"unknown";const r=[],s=[];let u=[];for(const n of e)"op"===n.kind?(r.push(u),s.push(n.value),u=[]):u.push(n.value);r.push(u);const i=r[r.length-1];if(void 0!==i&&0===i.length&&";"===s[s.length-1]&&r.pop(),0===r.length||r.some(n=>0===n.length))return"unknown";let a=!0;for(const n of r){const e=c(n,o,t);if("dangerous"===e)return"dangerous";"known-safe"!==e&&(a=!1)}return a?"known-safe":"unknown"}
@@ -1 +1 @@
1
- import{isEscapingPathArg as t}from"./path-audit.js";const e=new Set(["-exec","-execdir","-ok","-okdir","-delete","-fls","-fprint","-fprint0","-fprintf"]),r=t=>{for(const r of t.slice(1))if(e.has(r))return"reject";return"safe"};function n(e,r){for(let n=1;n<e.length;n+=1){const s=e[n];if(void 0!==s)for(const f of r){if(s===f){const r=e[n+1];if(void 0===r||t(r))return!0;n+=1;break}if(s.startsWith(`${f}=`)&&t(s.slice(f.length+1)))return!0;if(2===f.length&&s.startsWith(f)&&t(s.slice(2)))return!0}}return!1}const s=["-f","--file","--exclude-from"],f=t=>n(t,s)?"reject":"safe",o=new Set(["--search-zip","-z"]),i=["--pre","--hostname-bin"],c=["-f","--file","--ignore-file"],u=t=>{for(const e of t.slice(1)){if(o.has(e))return"reject";for(const t of i)if(e===t||e.startsWith(`${t}=`))return"reject"}return n(t,c)?"reject":"safe"},a=t=>{for(const e of t.slice(1))if("--output"===e||e.startsWith("--output=")||e.startsWith("-o"))return"reject";return"safe"},l=/^(\d+,)?\d+p$/,h=t=>{const e=t[2];return t.length<=4&&"-n"===t[1]&&void 0!==e&&l.test(e)?"safe":"reject"};export const FLAG_AUDITORS={find:r,grep:f,rg:u,base64:a,sed:h};export function auditFlags(t,e){const r=FLAG_AUDITORS[t];return r?r(e):"safe"}
1
+ function e(e,t){const n=new Set(t.booleanShort??""),o=new Set(t.valueShort??"");let i=!1;for(let r=1;r<e.length;r+=1){const l=e[r];if(void 0===l)return"reject";if("--"===l){i=!0;continue}if(i||"-"===l||!l.startsWith("-"))continue;if(l.startsWith("--")){const n=l.indexOf("="),o=-1===n?l:l.slice(0,n);if(t.booleanLong?.has(o)){if(-1!==n)return"reject";continue}if(!t.valueLong?.has(o))return"reject";if(-1!==n){if(0===l.slice(n+1).length)return"reject";continue}if(void 0===e[r+1])return"reject";r+=1;continue}const a=l.slice(1);if(0!==a.length)for(let t=0;t<a.length;t+=1){const i=a[t];if(void 0===i)return"reject";if(!n.has(i)){if(!o.has(i)||0!==t)return"reject";if(1===a.length){if(void 0===e[r+1])return"reject";r+=1}break}}}return"safe"}const t=new Set(["-iname","-maxdepth","-mindepth","-name","-type"]),n=new Set(["!","(",")","-a","-and","-empty","-not","-o","-or","-print","-print0"]),o=e=>{let o=!1;for(let i=1;i<e.length;i+=1){const r=e[i];if(void 0===r||"--"===r||"-H"===r||"-L"===r)return"reject";if(!o){if("-P"===r)continue;if(!r.startsWith("-")&&"!"!==r&&"("!==r)continue;o=!0}if(!n.has(r)){if(!t.has(r)||void 0===e[i+1])return"reject";i+=1}}return"safe"},i=t=>e(t,{booleanShort:"EFGHILPabcghijklnoqrsvwxy",valueShort:"ABCDefm",booleanLong:new Set(["--basic-regexp","--extended-regexp","--files-with-matches","--files-without-match","--fixed-strings","--ignore-case","--invert-match","--line-number","--no-filename","--only-matching","--perl-regexp","--quiet","--recursive","--silent","--text","--with-filename","--word-regexp","--line-regexp"]),valueLong:new Set(["--after-context","--before-context","--binary-files","--context","--devices","--directories","--exclude","--exclude-dir","--exclude-from","--file","--include","--label","--max-count","--regexp"])}),r=t=>e(t,{booleanShort:"FHIJNPSUVachilnopqsuvwxy",valueShort:"ABCEMTdefgjmrt",booleanLong:new Set(["--case-sensitive","--column","--count","--files-with-matches","--files-without-match","--fixed-strings","--heading","--hidden","--ignore-case","--invert-match","--line-number","--multiline","--no-heading","--no-ignore","--no-line-number","--only-matching","--pcre2","--quiet","--smart-case","--text","--unrestricted","--word-regexp","--line-regexp"]),valueLong:new Set(["--after-context","--before-context","--context","--encoding","--file","--glob","--ignore-file","--max-columns","--max-count","--max-depth","--regexp","--replace","--threads","--type","--type-not"])}),l=t=>e(t,{booleanShort:"Dd",booleanLong:new Set(["--decode"])}),a=t=>e(t,{booleanShort:"ABCFGRSUabcdfghiklmnopqrstux1",booleanLong:new Set(["--all","--almost-all","--classify","--directory","--human-readable","--inode","--long","--numeric-uid-gid","--recursive","--reverse","--size"])}),c=t=>e(t,{booleanShort:"Lclmw",booleanLong:new Set(["--bytes","--chars","--lines","--max-line-length","--words"])}),s=t=>e(t,{booleanShort:"fqv",valueShort:"cn",booleanLong:new Set(["--quiet","--silent","--verbose"]),valueLong:new Set(["--bytes","--lines"])}),u=/^(\d+,)?\d+p$/,f=e=>{const t=e[2];return e.length<=4&&"-n"===e[1]&&void 0!==t&&u.test(t)?"safe":"reject"};export const FLAG_AUDITORS={base64:l,find:o,grep:i,ls:a,rg:r,sed:f,tail:s,wc:c};export function auditFlags(e,t){const n=FLAG_AUDITORS[e];return n?n(t):"safe"}
@@ -1,2 +1,10 @@
1
1
  import type { AuditVerdict } from "./types.ts";
2
- export declare function auditGit(argv: readonly string[]): AuditVerdict;
2
+ /**
3
+ * Git is intentionally never auto-approved.
4
+ *
5
+ * Even read-looking subcommands can execute helpers configured through the
6
+ * repository, user config, attributes, or inherited environment. The shell
7
+ * policy must therefore ask for confirmation instead of treating argv alone
8
+ * as proof that a Git invocation is side-effect free.
9
+ */
10
+ export declare function auditGit(_argv: readonly string[], _workdir: string): AuditVerdict;
@@ -1 +1 @@
1
- const t=new Set(["status","log","diff","show","branch"]),e=new Set(["-C","-c","-p","--paginate","--config-env","--exec-path","--git-dir","--namespace","--super-prefix","--work-tree"]),r=["--config-env=","--exec-path=","--git-dir=","--namespace=","--super-prefix=","--work-tree="];function n(t){return!!e.has(t)||(!!((t.startsWith("-C")||t.startsWith("-c"))&&t.length>2)||r.some(e=>t.startsWith(e)))}function s(t){return"--output"===t||t.startsWith("--output=")||"--ext-diff"===t||"--textconv"===t||"--exec"===t||t.startsWith("--exec=")}const i=new Set(["--list","-l","--show-current","-a","--all","-r","--remotes","-v","-vv","--verbose"]);function o(t){for(const e of t)if(!i.has(e)&&!e.startsWith("--format="))return"reject";return"safe"}export function auditGit(e){const r=e.slice(1);let i=0;for(;i<r.length;){const t=r[i];if(void 0===t)return"reject";if(n(t))return"reject";if(!t.startsWith("-"))break;i+=1}const c=r[i];if(void 0===c||!t.has(c))return"reject";const a=r.slice(i+1);for(const t of a)if(s(t))return"reject";return"branch"===c?o(a):"safe"}
1
+ export function auditGit(t,e){return"reject"}
@@ -1,3 +1,2 @@
1
1
  import type { CommandClassifier } from "../../types/command-classification.ts";
2
- export declare function unwrapShellWrapper(command: string, platform: NodeJS.Platform): string;
3
2
  export declare const ruleBasedClassifier: CommandClassifier;
@@ -1 +1 @@
1
- import{classifyScript as r}from"./compound.js";import{executableLookupKey as e}from"./lookup-key.js";import{tokenizeScript as o}from"./tokenize.js";const n=new Set(["bash","sh","zsh"]),s=new Set(["-c","-lc","-lic"]);export function unwrapShellWrapper(r,t){const l=o(r);if(null===l||3!==l.length)return r;const[p,a,i]=l;return"word"===p?.kind&&"word"===a?.kind&&"word"===i?.kind&&n.has(e(p.value,t))&&s.has(a.value)?i.value:r}export const ruleBasedClassifier={classify(e){const o=process.platform;return r(unwrapShellWrapper(e,o),o)}};
1
+ import{classifyScript as s}from"./compound.js";export const ruleBasedClassifier={classify(o,r){const e=process.platform;return s(o,e,r)}};
@@ -1,3 +1,3 @@
1
1
  import type { AuditVerdict } from "./types.ts";
2
2
  export declare function isEscapingPathArg(arg: string): boolean;
3
- export declare function auditPathArgs(key: string, argv: readonly string[]): AuditVerdict;
3
+ export declare function auditPathArgs(key: string, argv: readonly string[], workdir: string): AuditVerdict;
@@ -1 +1 @@
1
- const t=new Set(["echo","expr","false","true","id","pwd","seq","tr","uname","whoami","which"]),e=new Set(["grep","rg"]);export function isEscapingPathArg(t){return!(!t.startsWith("/")&&!t.startsWith("~"))||t.split("/").includes("..")}export function auditPathArgs(r,s){if(t.has(r))return"safe";let i=!e.has(r);for(const t of s.slice(1))if(!t.startsWith("-"))if(i){if(isEscapingPathArg(t))return"reject"}else i=!0;return"safe"}
1
+ import{lstatSync as e,realpathSync as t}from"node:fs";import{isAbsolute as r,relative as n,resolve as s}from"node:path";const i=new Set(["echo","expr","false","true","id","pwd","seq","tr","uname","whoami","which"]),f=new Set(["grep","rg","sed"]),c={grep:["-f","--file","--exclude-from"],rg:["-f","--file","--ignore-file"]},o={grep:["-e","--regexp","-f","--file"],rg:["-e","--regexp","-f","--file"]};export function isEscapingPathArg(e){return!(!e.startsWith("/")&&!e.startsWith("~"))||e.split("/").includes("..")}function u(e,t){const s=n(e,t);return""===s||!s.startsWith("..")&&!r(s)}function a(r,n){if(isEscapingPathArg(r))return"reject";try{e(s(n,r))}catch{return"reject"}try{const e=t(n);return u(e,t(s(n,r)))?"safe":"reject"}catch{return"reject"}}function l(e,t,r){for(const n of r){if(e===n)return void 0===t?void 0:{value:t,consumesNext:!0};if(e.startsWith(`${n}=`))return{value:e.slice(n.length+1),consumesNext:!1};if(2===n.length&&e.startsWith(n)&&e.length>2)return{value:e.slice(2),consumesNext:!1}}}function h(e,t){let r=!1;for(const n of e.slice(1)){if("--"===n)return"reject";if(r||"-H"!==n&&"-L"!==n&&"-P"!==n){if(n.startsWith("-")||"!"===n||"("===n)break;if(r=!0,"safe"!==a(n,t))return"reject"}}return"safe"}export function auditPathArgs(e,t,r){if(i.has(e))return"safe";if("find"===e)return h(t,r);const n=c[e]??[],s=o[e]??[];let u=!f.has(e),g=!1;for(let e=1;e<t.length;e+=1){const i=t[e];if(void 0===i)return"reject";if(g||"--"!==i){if(!g&&i.startsWith("-")){const f=t[e+1],c=l(i,f,n);if(void 0!==c){if("safe"!==a(c.value,r))return"reject";(s.includes(i)||s.some(e=>i.startsWith(e)))&&(u=!0),c.consumesNext&&(e+=1);continue}const o=l(i,f,s);void 0!==o&&(u=!0,o.consumesNext&&(e+=1));continue}if(u){if("safe"!==a(i,r))return"reject"}else u=!0}else g=!0}return"safe"}
@@ -1 +1,7 @@
1
+ /**
2
+ * Auto-approved commands execute with the same fixed system PATH. Checking the
3
+ * candidate here prevents workspace/node_modules PATH shadowing from turning a
4
+ * read-only command name into arbitrary code execution.
5
+ */
6
+ export declare function isTrustedExecutable(key: string, platform: NodeJS.Platform): boolean;
1
7
  export declare function isSafeExecutable(key: string, platform: NodeJS.Platform): boolean;
@@ -1 +1 @@
1
- const e=["cat","cd","cut","echo","expr","false","grep","head","id","ls","nl","paste","pwd","rev","seq","stat","tail","tr","true","uname","uniq","wc","which","whoami"],t=["numfmt","tac"],a=new Set(e),n=new Set(t);export function isSafeExecutable(e,t){return!!a.has(e)||"linux"===t&&n.has(e)}
1
+ import{accessSync as t,constants as e}from"node:fs";import{join as n}from"node:path";const r=["cat","cut","echo","expr","false","grep","head","id","ls","nl","paste","pwd","rev","seq","stat","tail","tr","true","uname","uniq","wc","which","whoami"],s=["numfmt","tac"],a=new Set(r),i=new Set(s),u=new Set(["echo","false","pwd","true"]),o=["/usr/bin","/bin","/usr/sbin","/sbin"];export function isTrustedExecutable(r,s){if("win32"===s)return!1;if(u.has(r))return!0;for(const s of o)try{return t(n(s,r),e.X_OK),!0}catch{}return!1}export function isSafeExecutable(t,e){return!!a.has(t)||"linux"===e&&i.has(t)}
@@ -6,3 +6,5 @@ export declare const CLEAN_EXEC_ENV: {
6
6
  readonly LANG: "C.UTF-8";
7
7
  };
8
8
  export declare function withCleanEnv(base: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
9
+ /** Environment used only after a command has earned known-safe auto approval. */
10
+ export declare function withAutoApprovedShellEnv(base: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
@@ -1 +1 @@
1
- export const CLEAN_EXEC_ENV={NO_COLOR:"1",TERM:"dumb",PAGER:"cat",GIT_PAGER:"cat",LANG:"C.UTF-8"};export function withCleanEnv(E){return{...E,...CLEAN_EXEC_ENV}}
1
+ export const CLEAN_EXEC_ENV={NO_COLOR:"1",TERM:"dumb",PAGER:"cat",GIT_PAGER:"cat",LANG:"C.UTF-8"};const t="/usr/bin:/bin:/usr/sbin:/sbin",E=new Set(["BASH_ENV","BASHOPTS","CDPATH","ENV","GLOBIGNORE","GREP_OPTIONS","IFS","LD_AUDIT","LD_LIBRARY_PATH","LD_PRELOAD","PROMPT_COMMAND","PS4","RIPGREP_CONFIG_PATH","SHELLOPTS","ZDOTDIR"]);export function withCleanEnv(t){return{...t,...CLEAN_EXEC_ENV}}export function withAutoApprovedShellEnv(n){const A=withCleanEnv(n);for(const t of Object.keys(A))(E.has(t)||t.startsWith("BASH_FUNC_")||t.startsWith("DYLD_"))&&delete A[t];return A.PATH=t,A.SHELL="/bin/sh",A}
@@ -1,4 +1,4 @@
1
- import type { NormalizedToolResult } from "../tool-bridge/normalize-result.ts";
1
+ import { type NormalizedToolResult } from "../tool-bridge/normalize-result.ts";
2
2
  import type { CapturedStream } from "./output-buffer.ts";
3
3
  export declare const EXEC_TIMEOUT_EXIT_CODE = 124;
4
4
  export declare const EXIT_CODE_SIGNAL_BASE = 128;
@@ -1 +1 @@
1
- import{partitionModelBudget as t}from"./output-buffer.js";import{truncateMiddle as r}from"./truncate.js";export const EXEC_TIMEOUT_EXIT_CODE=124;export const EXIT_CODE_SIGNAL_BASE=128;export const BASH_TERMINATION_CAUSES={timeout:"timeout",abort:"abort"};const o=1;export function normalizeExitCode(t){return t.timedOut?124:null!==t.code?t.code:void 0!==t.signalNumber?128+t.signalNumber:1}function e(t,r){if(r.truncated)return`Warning: ${t} 输出已截断(原始 ${String(r.totalBytes)} 字节 / ${String(r.totalLines)} 行)`}function n(t,r){const o=r.length>0?r:void 0;return void 0!==o?`[${t}]\n${o}`:void 0}export function formatBashResult(o){const{result:i,maxModelOutputChars:s}=o;if(void 0!==i.spawnError)return{output:`命令无法启动: ${i.spawnError}`,isError:!0};const u=t(s,Array.from(i.stderr.text).length),d=r(i.stdout.text,u.stdout),a=r(i.stderr.text,u.stderr),m=[];i.terminationCause===BASH_TERMINATION_CAUSES.abort&&m.push("命令已中断(收到取消请求),不能视为正常完成"),i.timedOut&&m.push(i.terminationError?`命令超时(超过 ${String(i.timeoutMs)}ms),终止状态未确认`:`命令超时(超过 ${String(i.timeoutMs)}ms),已终止`),i.terminationError&&m.push(`终止失败: ${i.terminationError}`),m.push(`Exit code: ${String(i.exitCode)}`),m.push(`Wall time: ${(i.wallTimeMs/1e3).toFixed(1)} s`);const c=[i.stdout.truncated||d.truncated?e("stdout",i.stdout):void 0,i.stderr.truncated||a.truncated?e("stderr",i.stderr):void 0].filter(t=>void 0!==t);m.push(...c);const E=[n("stdout",d.text),n("stderr",a.text)].filter(t=>void 0!==t);return{output:[m.join("\n"),...E].join("\n\n"),isError:0!==i.exitCode||void 0!==i.terminationError}}
1
+ import{TOOL_OUTCOME_KINDS as t,failedToolResult as r,successfulToolResult as o}from"../tool-bridge/normalize-result.js";import{partitionModelBudget as e}from"./output-buffer.js";import{truncateMiddle as n}from"./truncate.js";export const EXEC_TIMEOUT_EXIT_CODE=124;export const EXIT_CODE_SIGNAL_BASE=128;export const BASH_TERMINATION_CAUSES={timeout:"timeout",abort:"abort"};const i=1;export function normalizeExitCode(t){return t.timedOut?124:null!==t.code?t.code:void 0!==t.signalNumber?128+t.signalNumber:1}function s(t,r){if(r.truncated)return`Warning: ${t} 输出已截断(原始 ${String(r.totalBytes)} 字节 / ${String(r.totalLines)} 行)`}function u(t,r){const o=r.length>0?r:void 0;return void 0!==o?`[${t}]\n${o}`:void 0}export function formatBashResult(i){const{result:d,maxModelOutputChars:a}=i;if(void 0!==d.spawnError)return r(t.toolFailed,`命令无法启动: ${d.spawnError}`,{raw:d});const m=e(a,Array.from(d.stderr.text).length),l=n(d.stdout.text,m.stdout),c=n(d.stderr.text,m.stderr),E=[];d.terminationCause===BASH_TERMINATION_CAUSES.abort&&E.push("命令已中断(收到取消请求),不能视为正常完成"),d.timedOut&&E.push(d.terminationError?`命令超时(超过 ${String(d.timeoutMs)}ms),终止状态未确认`:`命令超时(超过 ${String(d.timeoutMs)}ms),已终止`),d.terminationError&&E.push(`终止失败: ${d.terminationError}`),E.push(`Exit code: ${String(d.exitCode)}`),E.push(`Wall time: ${(d.wallTimeMs/1e3).toFixed(1)} s`);const p=[d.stdout.truncated||l.truncated?s("stdout",d.stdout):void 0,d.stderr.truncated||c.truncated?s("stderr",d.stderr):void 0].filter(t=>void 0!==t);E.push(...p);const x=[u("stdout",l.text),u("stderr",c.text)].filter(t=>void 0!==t),S=[E.join("\n"),...x].join("\n\n");return d.terminationCause===BASH_TERMINATION_CAUSES.abort?r(t.cancelled,S,{raw:d}):0!==d.exitCode||void 0!==d.terminationError?r(t.toolFailed,S,{raw:d}):o(S,{raw:d})}
@@ -1 +1 @@
1
- import{spawn as e,spawnSync as o}from"node:child_process";import{existsSync as t}from"node:fs";import{win32 as r}from"node:path";import{performance as n}from"node:perf_hooks";import{unknownCommandClassifier as i}from"../types/command-classification.js";import{ruleBasedClassifier as s}from"./classifier/index.js";import{DEFAULT_KILL_GRACE_MS as l,killProcessGroup as a}from"./kill.js";import{resolveUserShell as c}from"./shell.js";export const SHELL_PROFILE_IDS=["posix","powershell"];export const SHELL_TOOL_NAMES=["bash","powershell"];const u=["-NoLogo","-NoProfile","-NonInteractive","-ExecutionPolicy","Bypass","-EncodedCommand"],f=32767,d=1024,p=5e3,m=2e3,w=128,S=["PowerShell","7","pwsh.exe"],h=["System32","taskkill.exe"];function g(e,o){return o?.aborted?Promise.resolve(!1):new Promise(t=>{let r=!1;const n=e=>{r||(r=!0,clearTimeout(s),o?.removeEventListener("abort",i),t(e))},i=()=>n(!1),s=setTimeout(()=>n(!0),e);o?.addEventListener("abort",i,{once:!0})})}function E(e){const o=e.fileExists??t;return{id:"posix",toolName:"bash",supportsSessionExec:!0,supportsSafeCommandClassification:!0,waitForTreeKillAfterRootExit:!1,buildSpawn:(t,r,n)=>({file:c({platform:e.platform,env:e.env,fileExists:o}),args:["-c",t],options:{cwd:r,detached:!0,stdio:["ignore","pipe","pipe"],env:n}}),classify:(e,o)=>s.classify(e,o),async killTree(e,o,t={}){t.signal?.aborted||("interrupt"!==o?(a(e,"SIGTERM"),await g(l,t.signal)&&a(e,"SIGKILL")):a(e,"SIGINT"))},systemPromptHints:()=>["当前 shell 后端是 POSIX shell;优先使用 macOS/Linux 常见命令语法。","过滤和预览输出时可使用 grep、sed、head、tail 等 POSIX 工具。"]}}function v(e,o,t,r,n={}){return void 0===e||n.signal?.aborted?Promise.resolve():void 0===o?Promise.reject(new Error("无法从绝对 SystemRoot 路径解析 taskkill.exe,不能安全清理进程树")):new Promise((i,s)=>{let l,a=!1;const c=e=>{a||(a=!0,clearTimeout(f),n.signal?.removeEventListener("abort",u),e?s(e):i())},u=()=>{try{l.kill()}catch{}try{l.unref()}catch{}c()},f=setTimeout(()=>{try{l.kill()}catch{}try{l.unref()}catch{}c(new Error(`taskkill.exe 在 ${String(r)}ms 内未结束`))},r);try{l=t(o,["/PID",String(e),"/T","/F"],{stdio:"ignore",windowsHide:!0})}catch(e){return void c(new Error(`无法启动 taskkill.exe: ${x(e)}`,{cause:e}))}l.once("error",e=>{c(new Error(`无法启动 taskkill.exe: ${e.message}`,{cause:e}))}),l.once("close",(e,o)=>{if(0===e||e===w)return void c();const t=null===e?`signal=${o??"unknown"}`:`exitCode=${String(e)}`;c(new Error(`taskkill.exe 执行失败: ${t}`))}),n.signal?.addEventListener("abort",u,{once:!0}),n.signal?.aborted&&u()})}function x(e){return e instanceof Error?e.message:String(e)}export function buildPowerShellEncodedCommand(e){const o=["[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)","$OutputEncoding = [Console]::OutputEncoding","$ErrorActionPreference = 'Stop'","try {",e," if (Test-Path variable:LASTEXITCODE) { exit $LASTEXITCODE }","} catch {"," [Console]::Error.WriteLine([string]$_)"," exit 1","}"].join("\n");return Buffer.from(o,"utf16le").toString("base64")}function P(e,o){return[e,...o].reduce((e,o)=>e+o.length+1,0)}function b(e,o){const t=buildPowerShellEncodedCommand(e),r=[...u,t],n=P(o,r)+d;if(n>f)throw new Error(`PowerShell 命令过长:编码后命令行约 ${String(n)} 字符,超过 Windows CreateProcess 上限 ${String(f)};请拆分命令或改为执行脚本文件。`);return r}function k(e,o){const t=o.toLowerCase();for(const[o,r]of Object.entries(e))if(o.toLowerCase()===t&&void 0!==r&&r.length>0)return r}function y(e){const o=e.trim();return o.startsWith('"')&&o.endsWith('"')?o.slice(1,-1).trim():o}function C(e){return r.isAbsolute(e)&&r.parse(e).root.length>1}function T(e){const o=[],t=new Set,n=e=>{if(!C(e))return;const n=r.normalize(e).toLowerCase();t.has(n)||(t.add(n),o.push(r.normalize(e)))},i=k(e.env,"Path");for(const e of i?.split(r.delimiter)??[]){const o=y(e);C(o)&&n(r.join(o,"pwsh.exe"))}const s=[k(e.env,"ProgramFiles"),k(e.env,"ProgramW6432"),k(e.env,"ProgramFiles(x86)")];for(const e of s){if(void 0===e)continue;const o=y(e);C(o)&&n(r.join(o,...S))}return o}function L(e){const o=k(e,"SystemRoot");if(void 0===o)return;const t=y(o);return C(t)?r.join(t,...h):void 0}function I(e,o,t){const r=o(e,["-NoLogo","-NoProfile","-NonInteractive","-Command","$PSVersionTable.PSVersion.Major"],{encoding:"utf8",timeout:t,windowsHide:!0});if(r.error||0!==r.status)return;const n=`${r.stdout??""}`.trim(),i=Number.parseInt(n,10);return Number.isFinite(i)?i:void 0}function $(e,o){const r=e.fileExists??t,i=e.now??(()=>n.now()),s=i()+p;let l=!1;for(const t of T(e)){if(s-i()<=0)break;if(!r(t))continue;const e=Math.ceil(s-i());if(e<=0)break;const n=I(t,o,e);if(void 0!==n){if(n>=7)return{supported:!0,executable:t};l=!0}}return{supported:!1,reason:l?"pwsh-version-unsupported":"pwsh-not-found"}}function O(t){const r=$(t,t.spawnSync??o);if(!r.supported)return r;const n=t.spawn??e,s=L(t.env),l=t.taskkillTimeoutMs??m;return{supported:!0,profile:{id:"powershell",toolName:"powershell",supportsSessionExec:!0,supportsSafeCommandClassification:!1,waitForTreeKillAfterRootExit:!0,buildSpawn:(e,o,t)=>({file:r.executable,args:b(e,r.executable),options:{cwd:o,detached:!1,stdio:["ignore","pipe","pipe"],env:t,windowsHide:!0}}),classify:(e,o)=>i.classify(e,o),async killTree(e,o,t={}){await v(e,s,n,l,t)},systemPromptHints:()=>["当前 shell 后端是 PowerShell 7;请使用 PowerShell 语法,例如 Get-ChildItem、Get-Content、Select-String。","过滤和预览输出时优先使用 Select-String、Select-Object -First、Get-Content -TotalCount。"]}}}export function resolveShellProfile(e){return"win32"===e.platform?O(e):{supported:!0,profile:E(e)}}
1
+ import{spawn as e,spawnSync as o}from"node:child_process";import{existsSync as t}from"node:fs";import{win32 as r}from"node:path";import{performance as n}from"node:perf_hooks";import{unknownCommandClassifier as i}from"../types/command-classification.js";import{ruleBasedClassifier as s}from"./classifier/index.js";import{DEFAULT_KILL_GRACE_MS as l,killProcessGroup as a}from"./kill.js";import{resolveUserShell as c}from"./shell.js";export const SHELL_PROFILE_IDS=["posix","powershell"];export const SHELL_TOOL_NAMES=["bash","powershell"];const u=["-NoLogo","-NoProfile","-NonInteractive","-ExecutionPolicy","Bypass","-EncodedCommand"],f=32767,d=1024,p=5e3,m=2e3,w=128,S=["PowerShell","7","pwsh.exe"],h=["System32","taskkill.exe"];function g(e,o){return o?.aborted?Promise.resolve(!1):new Promise(t=>{let r=!1;const n=e=>{r||(r=!0,clearTimeout(s),o?.removeEventListener("abort",i),t(e))},i=()=>n(!1),s=setTimeout(()=>n(!0),e);o?.addEventListener("abort",i,{once:!0})})}function E(e){const o=e.fileExists??t;return{id:"posix",toolName:"bash",supportsSessionExec:!0,supportsSafeCommandClassification:!0,waitForTreeKillAfterRootExit:!1,buildSpawn:(t,r,n)=>({file:c({platform:e.platform,env:n,fileExists:o}),args:["-c",t],options:{cwd:r,detached:!0,stdio:["ignore","pipe","pipe"],env:n}}),classify:(e,o)=>s.classify(e,o),async killTree(e,o,t={}){t.signal?.aborted||("interrupt"!==o?(a(e,"SIGTERM"),await g(l,t.signal)&&a(e,"SIGKILL")):a(e,"SIGINT"))},systemPromptHints:()=>["当前 shell 后端是 POSIX shell;优先使用 macOS/Linux 常见命令语法。","过滤和预览输出时可使用 grep、sed、head、tail 等 POSIX 工具。"]}}function x(e,o,t,r,n={}){return void 0===e||n.signal?.aborted?Promise.resolve():void 0===o?Promise.reject(new Error("无法从绝对 SystemRoot 路径解析 taskkill.exe,不能安全清理进程树")):new Promise((i,s)=>{let l,a=!1;const c=e=>{a||(a=!0,clearTimeout(f),n.signal?.removeEventListener("abort",u),e?s(e):i())},u=()=>{try{l.kill()}catch{}try{l.unref()}catch{}c()},f=setTimeout(()=>{try{l.kill()}catch{}try{l.unref()}catch{}c(new Error(`taskkill.exe 在 ${String(r)}ms 内未结束`))},r);try{l=t(o,["/PID",String(e),"/T","/F"],{stdio:"ignore",windowsHide:!0})}catch(e){return void c(new Error(`无法启动 taskkill.exe: ${v(e)}`,{cause:e}))}l.once("error",e=>{c(new Error(`无法启动 taskkill.exe: ${e.message}`,{cause:e}))}),l.once("close",(e,o)=>{if(0===e||e===w)return void c();const t=null===e?`signal=${o??"unknown"}`:`exitCode=${String(e)}`;c(new Error(`taskkill.exe 执行失败: ${t}`))}),n.signal?.addEventListener("abort",u,{once:!0}),n.signal?.aborted&&u()})}function v(e){return e instanceof Error?e.message:String(e)}export function buildPowerShellEncodedCommand(e){const o=["[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)","$OutputEncoding = [Console]::OutputEncoding","$ErrorActionPreference = 'Stop'","try {",e," if (Test-Path variable:LASTEXITCODE) { exit $LASTEXITCODE }","} catch {"," [Console]::Error.WriteLine([string]$_)"," exit 1","}"].join("\n");return Buffer.from(o,"utf16le").toString("base64")}function P(e,o){return[e,...o].reduce((e,o)=>e+o.length+1,0)}function b(e,o){const t=buildPowerShellEncodedCommand(e),r=[...u,t],n=P(o,r)+d;if(n>f)throw new Error(`PowerShell 命令过长:编码后命令行约 ${String(n)} 字符,超过 Windows CreateProcess 上限 ${String(f)};请拆分命令或改为执行脚本文件。`);return r}function k(e,o){const t=o.toLowerCase();for(const[o,r]of Object.entries(e))if(o.toLowerCase()===t&&void 0!==r&&r.length>0)return r}function y(e){const o=e.trim();return o.startsWith('"')&&o.endsWith('"')?o.slice(1,-1).trim():o}function C(e){return r.isAbsolute(e)&&r.parse(e).root.length>1}function T(e){const o=[],t=new Set,n=e=>{if(!C(e))return;const n=r.normalize(e).toLowerCase();t.has(n)||(t.add(n),o.push(r.normalize(e)))},i=k(e.env,"Path");for(const e of i?.split(r.delimiter)??[]){const o=y(e);C(o)&&n(r.join(o,"pwsh.exe"))}const s=[k(e.env,"ProgramFiles"),k(e.env,"ProgramW6432"),k(e.env,"ProgramFiles(x86)")];for(const e of s){if(void 0===e)continue;const o=y(e);C(o)&&n(r.join(o,...S))}return o}function L(e){const o=k(e,"SystemRoot");if(void 0===o)return;const t=y(o);return C(t)?r.join(t,...h):void 0}function I(e,o,t){const r=o(e,["-NoLogo","-NoProfile","-NonInteractive","-Command","$PSVersionTable.PSVersion.Major"],{encoding:"utf8",timeout:t,windowsHide:!0});if(r.error||0!==r.status)return;const n=`${r.stdout??""}`.trim(),i=Number.parseInt(n,10);return Number.isFinite(i)?i:void 0}function $(e,o){const r=e.fileExists??t,i=e.now??(()=>n.now()),s=i()+p;let l=!1;for(const t of T(e)){if(s-i()<=0)break;if(!r(t))continue;const e=Math.ceil(s-i());if(e<=0)break;const n=I(t,o,e);if(void 0!==n){if(n>=7)return{supported:!0,executable:t};l=!0}}return{supported:!1,reason:l?"pwsh-version-unsupported":"pwsh-not-found"}}function O(t){const r=$(t,t.spawnSync??o);if(!r.supported)return r;const n=t.spawn??e,s=L(t.env),l=t.taskkillTimeoutMs??m;return{supported:!0,profile:{id:"powershell",toolName:"powershell",supportsSessionExec:!0,supportsSafeCommandClassification:!1,waitForTreeKillAfterRootExit:!0,buildSpawn:(e,o,t)=>({file:r.executable,args:b(e,r.executable),options:{cwd:o,detached:!1,stdio:["ignore","pipe","pipe"],env:t,windowsHide:!0}}),classify:(e,o)=>i.classify(e,o),async killTree(e,o,t={}){await x(e,s,n,l,t)},systemPromptHints:()=>["当前 shell 后端是 PowerShell 7;请使用 PowerShell 语法,例如 Get-ChildItem、Get-Content、Select-String。","过滤和预览输出时优先使用 Select-String、Select-Object -First、Get-Content -TotalCount。"]}}}export function resolveShellProfile(e){return"win32"===e.platform?O(e):{supported:!0,profile:E(e)}}
@@ -18,6 +18,7 @@ export interface SessionManagerOptions {
18
18
  export interface SpawnRequest {
19
19
  readonly command: string;
20
20
  readonly workdir: string;
21
+ readonly env?: NodeJS.ProcessEnv;
21
22
  readonly onDelta?: (stream: BashStreamName, delta: string) => void;
22
23
  }
23
24
  export interface SessionCleanupResult {
@@ -1 +1 @@
1
- import{performance as t}from"node:perf_hooks";import{waitForPromiseSettlement as e}from"../../bounded-wait.js";import{spawnSession as s}from"./session-exec.js";import{isTerminalSessionState as r,SESSION_STATES as i,SESSION_TERMINATION_CAUSES as o}from"./types.js";const n=150,a=2500,l=2e3,c=1e3,d=50,p=120;export class SessionCapError extends Error{constructor(t){super(`会话数已达上限 ${String(t)},且无空闲会话可回收`),this.name="SessionCapError"}}function m(){return 1e3+Math.floor(99e3*Math.random())}function h(t){return t instanceof Error?t.message:String(t)}function u(t){const e=Array.from(t,t=>{const e=t.codePointAt(0)??0;return e<32||127===e?" ":t}).join("").replace(/\s+/gu," ").trim(),s=Array.from(e);return s.length<=p?e:`${s.slice(0,119).join("")}…`}export class SessionManager{sessions=new Map;terminations=new Map;options;acceptingSessions=!0;constructor(t){this.options=t}spawn(t){if(!this.acceptingSessions)throw new Error("会话管理器已关闭,不再接受新命令");if(this.trimTombstones(),this.size()>=this.options.maxSessions)throw new SessionCapError(this.options.maxSessions);const e=this.allocateId(),r=s({id:e,command:t.command,workdir:t.workdir,profile:this.options.profile,env:this.options.env,bufferCapacity:this.options.bufferCapacity,...t.onDelta?{onDelta:t.onDelta}:{}});return this.sessions.set(e,r),this.observeLifecycle(r),r}get(t){return this.sessions.get(t)}delete(t){const e=this.sessions.get(t);return!(void 0===e||!r(e.state))&&this.sessions.delete(t)}size(){let t=0;for(const e of this.sessions.values())e.state!==i.completed&&(t+=1);return t}list(){const e=t.now();return[...this.sessions.values()].sort((t,e)=>t.startedAt-e.startedAt).map(t=>({sessionId:t.id,commandPreview:u(t.command),workdir:t.workdir,state:t.state,startedAt:t.startedAt,lastUsedAt:t.lastUsedAt,wallTimeMs:(t.completedAt??e)-t.startedAt,...void 0!==t.exitCode?{exitCode:t.exitCode}:{},...t.terminationCause?{terminationCause:t.terminationCause}:{},...t.cleanupError?{cleanupError:t.cleanupError}:{}}))}async interrupt(t){return this.stopTargets(t,o.interrupt)}async terminate(t){return this.stopTargets(t,o.terminate)}async interruptAll(){return this.stopTargets(this.activeIds(),o.interrupt)}async terminateAll(){return this.stopTargets(this.activeIds(),o.terminate)}async close(){return this.acceptingSessions=!1,this.terminateAll()}activeIds(){return[...this.sessions.values()].filter(t=>!r(t.state)).map(t=>t.id)}async stopTargets(t,e){const s="number"==typeof t?[t]:[...new Set(t)],i=[];for(const t of s){const s=this.sessions.get(t);void 0===s||r(s.state)||i.push(this.requestStop(s,e))}return Promise.all(i)}requestStop(t,e,s){const r=this.terminations.get(t.id);if(void 0!==r)return e===o.terminate&&(r.intent=e),void 0===s||r.errors.includes(s)||r.errors.push(s),r.promise;t.markStopping(e);const n={intent:e,errors:void 0===s?[]:[s],promise:Promise.resolve({sessionId:t.id,state:i.cleanupFailed})};return this.terminations.set(t.id,n),n.promise=this.runTermination(t,n).finally(()=>{this.terminations.delete(t.id),this.trimTombstones()}),n.promise}async runTermination(t,s){const i=s.intent,a=await this.killTree(t,i);let p=void 0!==a;if(void 0!==a&&s.errors.push(a),i===o.interrupt&&!t.closeObserved){s.intent!==o.terminate&&await e(t.waitClose(),this.options.interruptGraceMs??n);const r=t.exitObserved&&!0===t.profile.waitForTreeKillAfterRootExit;if(!t.closeObserved&&!r){const e=await this.killTree(t,o.terminate);void 0!==e&&(p=!0,s.errors.push(e))}}const m=this.options.rootSettleTimeoutMs??c;if(t.exitObserved||p||await e(t.waitExit(),m),!t.exitObserved){const r=this.forceKillRoot(t);void 0!==r&&s.errors.push(r),await e(t.waitExit(),m)}t.exitObserved||s.errors.push("根进程在强制终止请求后仍未确认退出"),t.closeObserved||await e(t.waitClose(),this.options.closeDrainTimeoutMs??l),t.closeObserved||(this.destroyStreams(t),await e(t.waitClose(),d)),t.closeObserved||s.errors.push("进程树终止后仍未观察到根进程退出与 stdio close"),s.errors.length>0?t.markCleanupFailed(s.errors.join(";")):t.markCompleted(),r(t.state)||t.markCleanupFailed("会话清理未能收口");const h=t.state;if(!r(h))throw new Error(`会话 ${String(t.id)} 清理后仍处于非终态`);return{sessionId:t.id,state:h,...t.cleanupError?{cleanupError:t.cleanupError}:{}}}async killTree(t,e){if(t.exitObserved&&!0===t.profile.waitForTreeKillAfterRootExit)return"根进程已退出,不能安全复用旧 PID 清理或确认后代进程树";const s=this.options.killTreeTimeoutMs??a,r=new AbortController;let i;try{const o=t.profile.killTree(t.child.pid,e,{signal:r.signal});return void await Promise.race([o,new Promise((t,e)=>{i=setTimeout(()=>{e(new Error(`进程树终止在 ${String(s)}ms 内未完成`)),r.abort()},s)})])}catch(t){return h(t)}finally{void 0!==i&&clearTimeout(i),r.abort()}}forceKillRoot(t){if(!t.exitObserved)try{return t.child.kill("SIGKILL")?void 0:"根进程强制终止请求未被接受"}catch(t){return`根进程强制终止请求失败: ${h(t)}`}}destroyStreams(t){t.child.stdin?.destroy(),t.child.stdout?.destroy(),t.child.stderr?.destroy(),t.child.unref()}observeLifecycle(t){t.waitSettled().then(()=>this.trimTombstones()),t.waitExit().then(()=>{if(t.closeObserved||r(t.state))return;const s=this.options.closeDrainTimeoutMs??l;e(t.waitSettled(),s).then(e=>{e||r(t.state)||this.requestStop(t,o.terminate,`根进程退出后 stdio 在 ${String(s)}ms 内未关闭`).catch(e=>{t.markCleanupFailed(`会话自动清理失败:${h(e)}`)})})})}trimTombstones(){const t=[...this.sessions.values()].filter(t=>t.state===i.completed&&!this.terminations.has(t.id)).sort((t,e)=>(t.completedAt??0)-(e.completedAt??0)),e=t.length-this.options.maxSessions;for(let s=0;s<e;s+=1){const e=t[s];void 0!==e&&this.sessions.delete(e.id)}}allocateId(){const t=this.options.generateId??m;for(let e=0;e<1e3;e+=1){const e=t();if(!this.sessions.has(e))return e}throw new Error("无法分配唯一会话 id")}}
1
+ import{performance as t}from"node:perf_hooks";import{waitForPromiseSettlement as e}from"../../bounded-wait.js";import{spawnSession as s}from"./session-exec.js";import{isTerminalSessionState as r,SESSION_STATES as i,SESSION_TERMINATION_CAUSES as o}from"./types.js";const n=150,a=2500,l=2e3,c=1e3,d=50,p=120;export class SessionCapError extends Error{constructor(t){super(`会话数已达上限 ${String(t)},且无空闲会话可回收`),this.name="SessionCapError"}}function m(){return 1e3+Math.floor(99e3*Math.random())}function h(t){return t instanceof Error?t.message:String(t)}function u(t){const e=Array.from(t,t=>{const e=t.codePointAt(0)??0;return e<32||127===e?" ":t}).join("").replace(/\s+/gu," ").trim(),s=Array.from(e);return s.length<=p?e:`${s.slice(0,119).join("")}…`}export class SessionManager{sessions=new Map;terminations=new Map;options;acceptingSessions=!0;constructor(t){this.options=t}spawn(t){if(!this.acceptingSessions)throw new Error("会话管理器已关闭,不再接受新命令");if(this.trimTombstones(),this.size()>=this.options.maxSessions)throw new SessionCapError(this.options.maxSessions);const e=this.allocateId(),r=s({id:e,command:t.command,workdir:t.workdir,profile:this.options.profile,env:t.env??this.options.env,bufferCapacity:this.options.bufferCapacity,...t.onDelta?{onDelta:t.onDelta}:{}});return this.sessions.set(e,r),this.observeLifecycle(r),r}get(t){return this.sessions.get(t)}delete(t){const e=this.sessions.get(t);return!(void 0===e||!r(e.state))&&this.sessions.delete(t)}size(){let t=0;for(const e of this.sessions.values())e.state!==i.completed&&(t+=1);return t}list(){const e=t.now();return[...this.sessions.values()].sort((t,e)=>t.startedAt-e.startedAt).map(t=>({sessionId:t.id,commandPreview:u(t.command),workdir:t.workdir,state:t.state,startedAt:t.startedAt,lastUsedAt:t.lastUsedAt,wallTimeMs:(t.completedAt??e)-t.startedAt,...void 0!==t.exitCode?{exitCode:t.exitCode}:{},...t.terminationCause?{terminationCause:t.terminationCause}:{},...t.cleanupError?{cleanupError:t.cleanupError}:{}}))}async interrupt(t){return this.stopTargets(t,o.interrupt)}async terminate(t){return this.stopTargets(t,o.terminate)}async interruptAll(){return this.stopTargets(this.activeIds(),o.interrupt)}async terminateAll(){return this.stopTargets(this.activeIds(),o.terminate)}async close(){return this.acceptingSessions=!1,this.terminateAll()}activeIds(){return[...this.sessions.values()].filter(t=>!r(t.state)).map(t=>t.id)}async stopTargets(t,e){const s="number"==typeof t?[t]:[...new Set(t)],i=[];for(const t of s){const s=this.sessions.get(t);void 0===s||r(s.state)||i.push(this.requestStop(s,e))}return Promise.all(i)}requestStop(t,e,s){const r=this.terminations.get(t.id);if(void 0!==r)return e===o.terminate&&(r.intent=e),void 0===s||r.errors.includes(s)||r.errors.push(s),r.promise;t.markStopping(e);const n={intent:e,errors:void 0===s?[]:[s],promise:Promise.resolve({sessionId:t.id,state:i.cleanupFailed})};return this.terminations.set(t.id,n),n.promise=this.runTermination(t,n).finally(()=>{this.terminations.delete(t.id),this.trimTombstones()}),n.promise}async runTermination(t,s){const i=s.intent,a=await this.killTree(t,i);let p=void 0!==a;if(void 0!==a&&s.errors.push(a),i===o.interrupt&&!t.closeObserved){s.intent!==o.terminate&&await e(t.waitClose(),this.options.interruptGraceMs??n);const r=t.exitObserved&&!0===t.profile.waitForTreeKillAfterRootExit;if(!t.closeObserved&&!r){const e=await this.killTree(t,o.terminate);void 0!==e&&(p=!0,s.errors.push(e))}}const m=this.options.rootSettleTimeoutMs??c;if(t.exitObserved||p||await e(t.waitExit(),m),!t.exitObserved){const r=this.forceKillRoot(t);void 0!==r&&s.errors.push(r),await e(t.waitExit(),m)}t.exitObserved||s.errors.push("根进程在强制终止请求后仍未确认退出"),t.closeObserved||await e(t.waitClose(),this.options.closeDrainTimeoutMs??l),t.closeObserved||(this.destroyStreams(t),await e(t.waitClose(),d)),t.closeObserved||s.errors.push("进程树终止后仍未观察到根进程退出与 stdio close"),s.errors.length>0?t.markCleanupFailed(s.errors.join(";")):t.markCompleted(),r(t.state)||t.markCleanupFailed("会话清理未能收口");const h=t.state;if(!r(h))throw new Error(`会话 ${String(t.id)} 清理后仍处于非终态`);return{sessionId:t.id,state:h,...t.cleanupError?{cleanupError:t.cleanupError}:{}}}async killTree(t,e){if(t.exitObserved&&!0===t.profile.waitForTreeKillAfterRootExit)return"根进程已退出,不能安全复用旧 PID 清理或确认后代进程树";const s=this.options.killTreeTimeoutMs??a,r=new AbortController;let i;try{const o=t.profile.killTree(t.child.pid,e,{signal:r.signal});return void await Promise.race([o,new Promise((t,e)=>{i=setTimeout(()=>{e(new Error(`进程树终止在 ${String(s)}ms 内未完成`)),r.abort()},s)})])}catch(t){return h(t)}finally{void 0!==i&&clearTimeout(i),r.abort()}}forceKillRoot(t){if(!t.exitObserved)try{return t.child.kill("SIGKILL")?void 0:"根进程强制终止请求未被接受"}catch(t){return`根进程强制终止请求失败: ${h(t)}`}}destroyStreams(t){t.child.stdin?.destroy(),t.child.stdout?.destroy(),t.child.stderr?.destroy(),t.child.unref()}observeLifecycle(t){t.waitSettled().then(()=>this.trimTombstones()),t.waitExit().then(()=>{if(t.closeObserved||r(t.state))return;const s=this.options.closeDrainTimeoutMs??l;e(t.waitSettled(),s).then(e=>{e||r(t.state)||this.requestStop(t,o.terminate,`根进程退出后 stdio 在 ${String(s)}ms 内未关闭`).catch(e=>{t.markCleanupFailed(`会话自动清理失败:${h(e)}`)})})})}trimTombstones(){const t=[...this.sessions.values()].filter(t=>t.state===i.completed&&!this.terminations.has(t.id)).sort((t,e)=>(t.completedAt??0)-(e.completedAt??0)),e=t.length-this.options.maxSessions;for(let s=0;s<e;s+=1){const e=t[s];void 0!==e&&this.sessions.delete(e.id)}}allocateId(){const t=this.options.generateId??m;for(let e=0;e<1e3;e+=1){const e=t();if(!this.sessions.has(e))return e}throw new Error("无法分配唯一会话 id")}}
@@ -1 +1 @@
1
- import{isAbsolute as t,relative as o}from"node:path";export function isWithinWorkdirRoot(r,i){const n=o(r,i);return""===n||!n.startsWith("..")&&!t(n)}
1
+ import{realpathSync as t}from"node:fs";import{isAbsolute as r,relative as o,sep as n}from"node:path";function i(t,i){const e=o(t,i);return""===e||!r(e)&&".."!==e&&!e.startsWith(`..${n}`)}export function isWithinWorkdirRoot(r,o){if(!i(r,o))return!1;try{return i(t(r),t(o))}catch{return!1}}
@@ -1,5 +1,5 @@
1
1
  import { type ModelMessage } from "ai";
2
- import type { LanguageModelV4, SharedV4ProviderOptions } from "@ai-sdk/provider";
2
+ import type { LanguageModelV4, LanguageModelV4CallOptions, SharedV4ProviderOptions } from "@ai-sdk/provider";
3
3
  import type { SkillLibrary, SkillSummary } from "@roll-agent/core/skills/library";
4
4
  import type { ContextCompactionReason, ContextCompactionStrategy, SessionEvent, SessionTokenUsage } from "../types/events.ts";
5
5
  import type { ToolPolicy } from "../types/policy.ts";
@@ -8,9 +8,18 @@ import { type AgentInstallToolCatalogEntry, type AgentInstallToolOutcome } from
8
8
  import { type SessionBashSettings } from "../tool-bridge/bash-tool.ts";
9
9
  import type { ShellProfile } from "../bash/profile.ts";
10
10
  import type { CommandClassifier } from "../types/command-classification.ts";
11
+ import { type RedactedToolExecutionRecordSummary, type ToolExecutionRecord } from "../tool-bridge/tool-execution-record.ts";
12
+ import type { CommitCompactionInput, ListToolExecutionsOptions, ListTranscriptMessagesOptions, SequencedToolExecutionRecord } from "../store/thread-store.ts";
13
+ import { type CapabilityAgentOnboardingCatalogEntry, type CapabilityExternalDynamicContext, type CapabilityHostMode, type EffectiveCapabilityTurnContext, type EffectiveCapabilityManifest } from "./capability-manifest.ts";
14
+ import { type ArchivedTranscriptMessage, type CompactionCheckpoint } from "./compaction-checkpoint.ts";
15
+ import { type TranscriptReader } from "../tool-bridge/transcript-tool.ts";
11
16
  export interface SessionCompactionSettings {
12
17
  readonly enabled: boolean;
13
18
  readonly strategy: ContextCompactionStrategy;
19
+ /** Total provider budget for schema-constrained checkpoint generation. */
20
+ readonly timeoutMs?: number;
21
+ /** AI SDK output budget for the structured checkpoint; reasoning accounting varies by provider. */
22
+ readonly maxOutputTokens?: number;
14
23
  readonly threshold: number;
15
24
  readonly keepRecentTurns: number;
16
25
  readonly keepRecentTokens: number;
@@ -24,14 +33,29 @@ export interface AgentSessionOptions {
24
33
  readonly initialMessages?: readonly ModelMessage[];
25
34
  readonly onPersist?: (messages: readonly ModelMessage[]) => void;
26
35
  readonly onReplace?: (messages: readonly ModelMessage[]) => void;
36
+ readonly onToolExecution?: (record: ToolExecutionRecord) => void;
37
+ readonly listToolExecutions?: (options?: ListToolExecutionsOptions) => readonly SequencedToolExecutionRecord[];
38
+ readonly getToolExecution?: (executionId: string) => SequencedToolExecutionRecord | undefined;
39
+ readonly initialCheckpoint?: CompactionCheckpoint;
40
+ readonly listTranscriptMessages?: (options?: ListTranscriptMessagesOptions) => readonly ArchivedTranscriptMessage[];
41
+ readonly commitCompaction?: (input: CommitCompactionInput) => CompactionCheckpoint;
42
+ readonly readCheckpointTranscript?: TranscriptReader;
27
43
  readonly contextWindow?: number;
28
44
  readonly compaction?: SessionCompactionSettings;
29
45
  readonly turnTimeoutMs?: number;
30
46
  readonly providerOptions?: SharedV4ProviderOptions;
47
+ readonly structuredOutputProviderOptions?: SharedV4ProviderOptions;
48
+ readonly structuredOutputReasoning?: NonNullable<LanguageModelV4CallOptions["reasoning"]>;
31
49
  /** `setProviderOptions()` 生效后触发;ConversationEngine 用它同步子 Agent Sampling。 */
32
50
  readonly onProviderOptionsChange?: (providerOptions: SharedV4ProviderOptions | undefined) => void;
33
51
  readonly debugEvents?: boolean;
52
+ /**
53
+ * 追加到 capability-driven 基础 prompt 之后的会话指令。
54
+ * 保留旧字段名以兼容调用方;它不能替换工具接地与能力清单。
55
+ */
34
56
  readonly systemPrompt?: string;
57
+ readonly capabilityContext?: AgentSessionCapabilityContext;
58
+ readonly resolveDynamicCapabilityContext?: (abortSignal: AbortSignal) => CapabilityExternalDynamicContext | Promise<CapabilityExternalDynamicContext>;
35
59
  readonly skillLibrary?: SkillLibrary;
36
60
  readonly bash?: SessionBashSettings;
37
61
  readonly bashClassifier?: CommandClassifier;
@@ -39,10 +63,21 @@ export interface AgentSessionOptions {
39
63
  readonly agentInstall?: AgentSessionAgentInstall;
40
64
  readonly onClose?: () => void;
41
65
  }
66
+ export interface AgentSessionCapabilityContext {
67
+ readonly profile: string;
68
+ readonly hostMode?: CapabilityHostMode;
69
+ readonly cwd: string;
70
+ readonly platform?: NodeJS.Platform;
71
+ readonly shellHints?: readonly string[];
72
+ readonly agentCount: number;
73
+ readonly agentOnboardingCatalog?: readonly CapabilityAgentOnboardingCatalogEntry[];
74
+ }
42
75
  export interface SessionAgentRefresh {
43
76
  readonly source: AgentToolSource;
44
77
  readonly skillLibrary?: SkillLibrary;
45
- readonly systemPrompt: string;
78
+ /** 追加到重新编译后的 capability-driven prompt,不能整体替换基础 prompt。 */
79
+ readonly systemPrompt?: string;
80
+ readonly capabilityContext?: AgentSessionCapabilityContext;
46
81
  }
47
82
  export interface AgentInstallSessionResult {
48
83
  readonly outcome: AgentInstallToolOutcome;
@@ -59,8 +94,12 @@ export interface AgentSessionBashSession {
59
94
  readonly defaultYieldMs: number;
60
95
  readonly maxOutputTokens: number;
61
96
  readonly bufferCapacity: number;
97
+ readonly env?: NodeJS.ProcessEnv;
62
98
  }
63
99
  export type SessionSkillSummary = SkillSummary;
100
+ export type SessionToolExecutionRecordView = SequencedToolExecutionRecord | (RedactedToolExecutionRecordSummary & {
101
+ readonly sequence: number;
102
+ });
64
103
  export declare class AgentSession {
65
104
  readonly id: string;
66
105
  private readonly model;
@@ -68,23 +107,43 @@ export declare class AgentSession {
68
107
  private readonly messages;
69
108
  private readonly onPersist;
70
109
  private readonly onReplace;
110
+ private readonly onToolExecution;
111
+ private readonly listPersistedToolExecutions;
112
+ private readonly getPersistedToolExecution;
113
+ private readonly listPersistedTranscriptMessages;
114
+ private readonly commitPersistedCompaction;
115
+ private readonly inMemoryToolExecutions;
116
+ private readonly inMemoryTranscript;
117
+ private readonly legacyV1ActiveSnapshot;
118
+ private readonly compactionResources;
71
119
  private readonly contextWindow;
72
120
  private readonly compaction;
73
121
  private readonly turnTimeoutMs;
74
122
  private readonly onClose;
75
123
  private providerOptions;
124
+ private readonly structuredOutputProviderOptions;
125
+ private readonly structuredOutputReasoning;
76
126
  private readonly onProviderOptionsChange;
77
127
  private readonly debugEvents;
78
128
  private readonly policy;
79
129
  private systemPrompt;
130
+ private readonly explicitSystemPrompt;
131
+ private capabilityContext;
132
+ private readonly resolveDynamicCapabilityContext;
133
+ private capabilityManifest;
134
+ private lastCapabilityTurnContext;
135
+ private readonly toolRoles;
80
136
  private skillSummaries;
81
137
  private skillLibrary;
82
138
  private skillToolBuilt;
83
139
  private readonly toolSourceAgentNames;
84
140
  private readonly gate;
141
+ private readonly toolCoordinator;
85
142
  private tools;
86
143
  private readonly registry;
87
144
  private readonly sessionManager;
145
+ private readonly sessionManagerInstanceId;
146
+ private compactionCheckpoint;
88
147
  private emit;
89
148
  private activeTurn;
90
149
  private closePromise;
@@ -93,30 +152,56 @@ export declare class AgentSession {
93
152
  private lastInputTokens;
94
153
  private needsCompaction;
95
154
  constructor(options: AgentSessionOptions);
155
+ private compileCapabilityManifest;
156
+ private compileSystemPrompt;
157
+ private refreshCapabilityManifest;
96
158
  private buildSkillTools;
97
159
  applyAgentRefresh(refresh: SessionAgentRefresh): void;
98
160
  send(input: string): AsyncIterable<SessionEvent>;
99
161
  compact(reason?: ContextCompactionReason): AsyncIterable<SessionEvent>;
100
162
  private runTurn;
163
+ private persistMessages;
164
+ private persistContextFailure;
165
+ private persistToolExecution;
166
+ private rememberCompactionResources;
167
+ private retainCompactionResource;
101
168
  private requestApproval;
102
169
  approve(approvalId: string): boolean;
103
170
  reject(approvalId: string, reason?: string): boolean;
104
171
  getMessages(): readonly ModelMessage[];
172
+ getToolExecutions(options?: ListToolExecutionsOptions, includeRaw?: boolean): readonly SessionToolExecutionRecordView[];
173
+ getToolExecution(executionId: string, includeRaw?: boolean): SessionToolExecutionRecordView | undefined;
174
+ getCapabilityManifest(): EffectiveCapabilityManifest;
175
+ getCapabilityTurnContext(): EffectiveCapabilityTurnContext | undefined;
105
176
  getContextWindow(): number | undefined;
106
177
  getSessionUsage(): SessionTokenUsage;
107
178
  getSkillSummaries(): readonly SessionSkillSummary[];
108
179
  setProviderOptions(providerOptions: SharedV4ProviderOptions | undefined): void;
109
180
  private shouldAutoCompact;
181
+ private listCompactionTranscriptEvidence;
182
+ private listCompactionToolEvidence;
183
+ private latestExplicitSkillNames;
184
+ private currentCompactionRunningWork;
185
+ private captureCompactionEvidence;
186
+ private buildCompactionDraft;
110
187
  private runCompactionTurn;
188
+ private advanceCompactionSemanticEvidenceWatermarks;
189
+ private countContiguousPresentedMessageEvidence;
111
190
  private runCompaction;
112
191
  private recoverFromContextError;
113
192
  private isTurnAborted;
114
193
  private execSessionIds;
194
+ private activeExecSessionIds;
115
195
  private interruptExecSessions;
116
196
  private abandonTurn;
117
- private cancellationMessage;
197
+ private currentCancellationActivity;
198
+ private captureCancellationActivity;
199
+ private cancellationActivity;
200
+ private cancellationDisplayMessage;
201
+ private cancellationContextMessage;
118
202
  private emitCancellation;
119
203
  private persistCancelledTurn;
204
+ private persistPendingToolCancellations;
120
205
  cancel(): boolean;
121
206
  close(): Promise<void>;
122
207
  abort(): void;
@@ -1 +1 @@
1
- import{randomUUID as e}from"node:crypto";import{waitForPromiseSettlement as t}from"../bounded-wait.js";import{stepCountIs as s,streamText as o}from"ai";import{buildAgentToolset as i}from"../tool-bridge/build-tools.js";import{buildAgentInstallToolset as n}from"../tool-bridge/agent-install-tool.js";import{buildSkillToolset as r}from"../tool-bridge/skill-tool.js";import{buildBashToolset as a}from"../tool-bridge/bash-tool.js";import{buildSessionExecToolset as l}from"../tool-bridge/session-exec-tool.js";import{SessionManager as c}from"../bash/session/session-manager.js";import{withCleanEnv as h}from"../bash/clean-env.js";import{RUNTIME_CANCELLATION_ABORT_REASON as u,SESSION_CANCELLATION_REASONS as p,TURN_TIMEOUT_ABORT_REASON as d,USER_CANCELLATION_ABORT_REASON as m,isTurnTimeoutAbortReason as g}from"../types/cancellation.js";import{ToolRegistry as b}from"../tool-bridge/naming.js";import{buildChatSystemPrompt as v}from"./system-prompt.js";import{readIsError as T}from"../tool-bridge/normalize-result.js";import{ApprovalGate as k}from"../approval/approval-gate.js";import{compactMessages as f}from"./compactor.js";import{AsyncEventQueue as y}from"./event-queue.js";const C=6e3;function x(){return{abortController:new AbortController,execSessionIds:new Set,aborted:!1,cancellationEventEmitted:!1,cancellationPersisted:!1}}function w(e){return e instanceof Error?e.message:String(e)}function S(e){return"object"==typeof e&&null!==e&&"output"in e}function I(e){return S(e)?w(e.output):w(e)}function R(e){const t=I(e);return t.startsWith("已取消执行")||t.startsWith("策略拒绝执行")?t:void 0}function A(e){return/context[_ -]?length|context window|maximum context|token limit|too many tokens|prompt is too long|input is too long/i.test(w(e))}function M(e){const t=e.inputTokenDetails?.cacheReadTokens,s=e.inputTokenDetails?.cacheWriteTokens,o=e.outputTokenDetails?.reasoningTokens;return{...void 0!==e.inputTokens?{inputTokens:e.inputTokens}:{},...void 0!==e.outputTokens?{outputTokens:e.outputTokens}:{},...void 0!==e.totalTokens?{totalTokens:e.totalTokens}:{},...void 0!==t?{cachedInputTokens:t}:{},...void 0!==s?{cacheWriteTokens:s}:{},...void 0!==o?{reasoningTokens:o}:{}}}function N(e,t){return void 0===e&&void 0===t?void 0:(e??0)+(t??0)}function E(e,t){const s=N(e.cachedInputTokens,t.cachedInputTokens),o=N(e.cacheWriteTokens,t.cacheWriteTokens),i=N(e.reasoningTokens,t.reasoningTokens);return{inputTokens:(e.inputTokens??0)+(t.inputTokens??0),outputTokens:(e.outputTokens??0)+(t.outputTokens??0),totalTokens:(e.totalTokens??0)+(t.totalTokens??0),...void 0!==s?{cachedInputTokens:s}:{},...void 0!==o?{cacheWriteTokens:o}:{},...void 0!==i?{reasoningTokens:i}:{}}}function P(e,t){return void 0===t?e:void 0===e?t:Math.max(e,t)}function j(e,t){const s=e.trim(),o=t.trim();return s.length>0&&o.length>0&&o.startsWith(s)}function W(e,t){return void 0!==e?e:g(t)?p.timeout:p.runtime}function $(e){return e.flatMap(e=>{if("assistant"!==e.role||!Array.isArray(e.content))return[e];const t=e.content.filter(e=>"reasoning"!==e.type);return 0===t.length?[]:t.length===e.content.length?[e]:[{...e,content:t}]})}export class AgentSession{id;model;maxSteps;messages;onPersist;onReplace;contextWindow;compaction;turnTimeoutMs;onClose;providerOptions;onProviderOptionsChange;debugEvents;policy;systemPrompt;skillSummaries;skillLibrary;skillToolBuilt=!1;toolSourceAgentNames;gate=new k;tools;registry;sessionManager;emit;activeTurn;closePromise;closed=!1;sessionUsage={inputTokens:0,outputTokens:0,totalTokens:0};lastInputTokens;needsCompaction=!1;constructor(e){this.id=e.id,this.model=e.model,this.maxSteps=e.maxSteps,this.messages=e.initialMessages?$(e.initialMessages):[],this.onPersist=e.onPersist,this.onReplace=e.onReplace,this.contextWindow=e.contextWindow,this.compaction=e.compaction,this.turnTimeoutMs=e.turnTimeoutMs,this.onClose=e.onClose,this.providerOptions=e.providerOptions,this.onProviderOptionsChange=e.onProviderOptionsChange,this.debugEvents=e.debugEvents??!1,this.policy=e.policy,this.systemPrompt=e.systemPrompt??v(),this.skillLibrary=e.skillLibrary,this.skillSummaries=e.skillLibrary?.list()??[],this.toolSourceAgentNames=new Set(e.sources.map(e=>e.agentName));const t=new b,s=e.skillLibrary?this.buildSkillTools(t):{},o={...e.policy?{policy:e.policy}:{},requestApproval:e=>this.requestApproval(e),emitEvent:e=>this.emit?.(e)},r=e.bashClassifier?{classifier:e.bashClassifier}:{},u=e.bash?a(e.bash,t,o,r):{};e.bashSession&&(this.sessionManager=new c({maxSessions:e.bashSession.maxSessions,profile:e.bashSession.profile,env:h(process.env),bufferCapacity:e.bashSession.bufferCapacity}));const p=e.bashSession&&this.sessionManager?l({workdir:e.bashSession.workdir,defaultYieldMs:e.bashSession.defaultYieldMs,maxOutputTokens:e.bashSession.maxOutputTokens},this.sessionManager,t,o,{...r,onSessionTouched:e=>this.activeTurn?.execSessionIds.add(e)}):{},d=e.agentInstall,m=d?n({catalog:d.catalog,install:async(e,t)=>{const s=await d.install(e,t);return s.outcome.ok&&s.refresh?(this.applyAgentRefresh(s.refresh),{...s.outcome,refreshApplied:!0}):s.outcome}},t,{...e.policy?{policy:e.policy}:{},requestApproval:e=>this.requestApproval(e)}):{},g=i(e.sources,{...e.policy?{policy:e.policy}:{},requestApproval:e=>this.requestApproval(e)},t);this.tools={...s,...u,...p,...m,...g.tools},this.registry=g.registry}buildSkillTools(e){return this.skillToolBuilt=!0,r(()=>{if(!this.skillLibrary)throw new Error("skill library 不可用");return this.skillLibrary},e)}applyAgentRefresh(e){if(!this.toolSourceAgentNames.has(e.source.agentName)){const t=i([e.source],{...this.policy?{policy:this.policy}:{},requestApproval:e=>this.requestApproval(e)},this.registry);this.tools={...this.tools,...t.tools},this.toolSourceAgentNames.add(e.source.agentName)}e.skillLibrary&&(this.skillLibrary=e.skillLibrary,this.skillSummaries=e.skillLibrary.list(),this.skillToolBuilt||(this.tools={...this.tools,...this.buildSkillTools(this.registry)})),this.systemPrompt=e.systemPrompt}async*send(e){if(this.closed)throw new Error("session is closed");if(this.activeTurn)throw new Error("session already has an active turn");const t=new y;this.emit=e=>t.push(e);const s=x();this.activeTurn=s,this.runTurn(t,s,e).catch(e=>{t.push({type:"error",stage:"execute",message:w(e)}),t.close()});try{for await(const e of t)yield e}finally{this.activeTurn===s&&this.abandonTurn(s),void 0!==this.emit&&(this.emit=void 0)}}async*compact(e="manual"){if(this.closed)throw new Error("session is closed");if(this.activeTurn)throw new Error("session already has an active turn");const t=new y;this.emit=e=>t.push(e);const s=x();this.activeTurn=s,this.runCompactionTurn(t,s,e).catch(e=>{t.push({type:"error",stage:"plan",message:w(e)}),t.close()});try{for await(const e of t)yield e}finally{this.activeTurn===s&&this.abandonTurn(s),void 0!==this.emit&&(this.emit=void 0)}}async runTurn(t,i,n){const r=Date.now();let a,l;try{if(this.debug(t,"turn","start",r,{messages:this.messages.length,tools:Object.keys(this.tools).length,maxSteps:this.maxSteps,...void 0!==this.contextWindow?{contextWindow:this.contextWindow}:{},...void 0!==this.lastInputTokens?{lastInputTokens:this.lastInputTokens}:{}}),this.shouldAutoCompact()){this.debug(t,"compaction","auto requested before turn",r,{messages:this.messages.length,...void 0!==this.lastInputTokens?{lastInputTokens:this.lastInputTokens}:{}});try{await this.runCompaction(t,"auto",i)}catch(e){return void t.push({type:"error",stage:"plan",message:w(e)})}}if(i.aborted||i.abortController.signal.aborted)return a=this.messages.length,this.messages.push({role:"user",content:n}),void this.persistCancelledTurn(t,i,a,[],r);a=this.messages.length,this.messages.push({role:"user",content:n}),t.push({type:"message-start",messageId:e()}),this.debug(t,"model","calling streamText",r,{messages:this.messages.length,tools:Object.keys(this.tools).length,...void 0!==this.turnTimeoutMs?{timeoutMs:this.turnTimeoutMs}:{}}),void 0!==this.turnTimeoutMs&&(l=setTimeout(()=>{i.abortController.signal.aborted||(i.aborted=!0,i.cancellationReason=p.timeout,this.gate.abortAll("本轮运行超时"),i.abortController.abort(d))},this.turnTimeoutMs));let c=[];const h=o({model:this.model,system:this.systemPrompt,messages:this.messages,tools:this.tools,stopWhen:s(this.maxSteps),abortSignal:i.abortController.signal,...this.providerOptions?{providerOptions:this.providerOptions}:{},onAbort:({steps:e})=>{c=e.flatMap(e=>e.response.messages)}});this.debug(t,"model","streamText returned",r);let u,m,b,v,k,f,y="",C="",x=!1,S=0,N=!1;const O=this.scheduleDebug(t,"model","waiting for first stream event",r,{messages:this.messages.length});try{for await(const e of h.fullStream){if(this.closed){i.aborted=!0,i.cancellationReason??=p.runtime;break}switch(N||(N=!0,this.clearDebugTimer(O),this.debug(t,"model","first stream event",r,{part:e.type})),e.type){case"text-delta":if(!x){const s=C+e.text;if(j(s,n)){C=s;break}if(C.length>0){const s=C+e.text;C="",y+=s,t.push({type:"text-delta",delta:s});break}}y+=e.text,t.push({type:"text-delta",delta:e.text});break;case"tool-call":{x=!0,C="";const s=this.registry.resolve(e.toolName);t.push({type:"tool-call",toolCallId:e.toolCallId,agentName:s?.agentName??e.toolName,toolName:s?.toolName??e.toolName,input:e.input});break}case"tool-result":{const s=this.registry.resolve(e.toolName),o=T(e.output);t.push({type:"tool-result",toolCallId:e.toolCallId,agentName:s?.agentName??e.toolName,toolName:s?.toolName??e.toolName,output:e.output,isError:o}),i.aborted||i.abortController.signal.aborted||!o||(f=R(e.output));break}case"tool-error":{const s=this.registry.resolve(e.toolName),o=I(e.error);t.push({type:"tool-result",toolCallId:e.toolCallId,agentName:s?.agentName??e.toolName,toolName:s?.toolName??e.toolName,output:o,isError:!0}),i.aborted||i.abortController.signal.aborted||(f=R(e.error));break}case"finish-step":{const s=M(e.usage);m=P(m,s.inputTokens),S+=1,v=e.finishReason;const o=e.performance.outputTokensPerSecond??e.performance.effectiveOutputTokensPerSecond;void 0!==o&&Number.isFinite(o)&&(b=o),t.push({type:"step-finish",finishReason:e.finishReason,usage:s});break}case"finish":!x&&C.length>0&&(y+=C,t.push({type:"text-delta",delta:C}),C=""),u=M(e.totalUsage);break;case"error":if(this.isTurnAborted(i)||g(e.error)){i.aborted=!0,i.cancellationReason=W(i.cancellationReason,e.error);break}A(e.error)&&(this.needsCompaction=!0),k=w(e.error),t.push({type:"error",stage:"execute",message:k});break;case"abort":i.aborted=!0,i.cancellationReason=W(i.cancellationReason,e.reason)}if(void 0!==f)break}}finally{this.clearDebugTimer(O)}if(this.debug(t,"model","fullStream finished",r,{textChars:y.length}),void 0!==k)return this.messages.splice(a),void(this.needsCompaction&&await this.recoverFromContextError(t,i));if(i.aborted||i.abortController.signal.aborted)return i.cancellationReason=W(i.cancellationReason,i.abortController.signal.reason),void this.persistCancelledTurn(t,i,a,c,r);if(void 0!==f){const e=f,s=0===y.length?e:`\n${e}`;t.push({type:"text-delta",delta:s}),this.messages.push({role:"assistant",content:e}),this.debug(t,"persist","persisting messages",r,{appendedMessages:this.messages.length-a}),this.onPersist?.(this.messages.slice(a)),this.debug(t,"persist","messages persisted",r,{totalMessages:this.messages.length}),u&&(this.sessionUsage=E(this.sessionUsage,u));const o=m??u?.inputTokens;return void 0!==o&&(this.lastInputTokens=o),void t.push({type:"message-finish",text:e,...u?{totalUsage:u}:{},sessionUsage:{...this.sessionUsage},...void 0!==m?{contextInputTokens:m}:{},...void 0!==b?{outputTokensPerSecond:b}:{}})}let D;this.debug(t,"model","awaiting response messages",r);const U=this.scheduleDebug(t,"model","still awaiting response messages",r);try{D=(await h.response).messages}catch(e){return this.clearDebugTimer(U),this.isTurnAborted(i)||g(e)?(i.aborted=!0,i.cancellationReason=W(i.cancellationReason,e),void this.persistCancelledTurn(t,i,a,c,r)):(A(e)&&(this.needsCompaction=!0),this.messages.splice(a),t.push({type:"error",stage:"execute",message:w(e)}),void(this.needsCompaction&&await this.recoverFromContextError(t,i)))}finally{this.clearDebugTimer(U)}if(this.debug(t,"model","response messages ready",r,{responseMessages:D.length}),this.isTurnAborted(i))return i.cancellationReason=W(i.cancellationReason,i.abortController.signal.reason),void this.persistCancelledTurn(t,i,a,c,r);const q=$(D);this.messages.push(...q),this.debug(t,"persist","persisting messages",r,{appendedMessages:this.messages.length-a}),this.onPersist?.(this.messages.slice(a)),this.debug(t,"persist","messages persisted",r,{totalMessages:this.messages.length}),u&&(this.sessionUsage=E(this.sessionUsage,u));const L=m??u?.inputTokens;void 0!==L&&(this.lastInputTokens=L);const F=S>=this.maxSteps&&"tool-calls"===v;t.push({type:"message-finish",text:y,...u?{totalUsage:u}:{},sessionUsage:{...this.sessionUsage},...void 0!==m?{contextInputTokens:m}:{},...void 0!==b?{outputTokensPerSecond:b}:{},...F?{stoppedAtStepLimit:!0}:{}})}catch(e){if(this.isTurnAborted(i)||g(e))return i.aborted=!0,i.cancellationReason=W(i.cancellationReason,i.abortController.signal.reason??e),void(void 0!==a?this.persistCancelledTurn(t,i,a,[],r):this.emitCancellation(t,i));void 0!==a&&this.messages.splice(a),A(e)&&(this.needsCompaction=!0),this.debug(t,"turn","error",r,{message:w(e)}),t.push({type:"error",stage:"execute",message:w(e)}),this.needsCompaction&&await this.recoverFromContextError(t,i)}finally{void 0!==l&&clearTimeout(l),this.activeTurn===i&&(this.activeTurn=void 0),t.close()}}requestApproval(t){const s=e(),o=this.gate.request(s);return void 0===this.emit?(this.gate.resolve(s,{approved:!1,reason:"approval event could not be delivered"}),o):(this.emit({type:"confirmation-required",approvalId:s,agentName:t.agentName,toolName:t.toolName,input:t.input,...t.reason?{reason:t.reason}:{}}),o)}approve(e){return this.gate.resolve(e,{approved:!0})}reject(e,t){return this.gate.resolve(e,{approved:!1,...t?{reason:t}:{}})}getMessages(){return this.messages}getContextWindow(){return this.contextWindow}getSessionUsage(){return{...this.sessionUsage}}getSkillSummaries(){return this.skillSummaries}setProviderOptions(e){this.providerOptions=e,this.onProviderOptionsChange?.(e)}shouldAutoCompact(){const e=this.compaction;return void 0!==e&&e.enabled&&(this.needsCompaction||void 0!==this.contextWindow&&void 0!==this.lastInputTokens&&this.lastInputTokens/this.contextWindow>=e.threshold)}async runCompactionTurn(e,t,s){try{await this.runCompaction(e,s,t)}catch(t){e.push({type:"error",stage:"plan",message:w(t)})}finally{this.activeTurn===t&&(this.activeTurn=void 0),e.close()}}async runCompaction(e,t,s){const o=Date.now(),i=this.compaction,n=i?.strategy??"summarize";if(this.debug(e,"compaction","start",o,{reason:t,messages:this.messages.length}),e.push({type:"compaction-start",reason:t}),this.isTurnAborted(s))return void(void 0!==s&&this.emitCancellation(e,s));if(!i)return void e.push({type:"context-compacted",reason:t,strategy:n,removed:0,kept:this.messages.length});const r=this.lastInputTokens;let a=i.strategy;const l=s?.abortController.signal;let c;try{c=await f({messages:this.messages,strategy:a,keepRecentTurns:i.keepRecentTurns,keepRecentTokens:i.keepRecentTokens,model:this.model,...l?{abortSignal:l}:{}})}catch(t){if(this.isTurnAborted(s))return void(void 0!==s&&this.emitCancellation(e,s));if("summarize"!==a)throw t;this.debug(e,"compaction","summarize failed, fallback to truncate",o,{message:w(t)}),a="truncate",c=await f({messages:this.messages,strategy:a,keepRecentTurns:i.keepRecentTurns,keepRecentTokens:i.keepRecentTokens,model:this.model,...l?{abortSignal:l}:{}})}if(this.isTurnAborted(s))return void(void 0!==s&&this.emitCancellation(e,s));const h=c.removed>0||c.truncatedTools>0;h&&(this.onReplace?.(c.messages),this.messages.splice(0,this.messages.length,...c.messages),this.lastInputTokens=void 0,this.needsCompaction=!1),e.push({type:"context-compacted",reason:t,strategy:a,removed:c.removed,kept:c.kept,...c.truncatedTools>0?{truncatedTools:c.truncatedTools}:{},...void 0!==r?{beforeInputTokens:r}:{}}),this.debug(e,"compaction","finish",o,{reason:t,strategy:a,removed:c.removed,kept:c.kept,truncatedTools:c.truncatedTools,progressed:h})}async recoverFromContextError(e,t){if(this.compaction?.enabled)try{await this.runCompaction(e,"auto",t)}catch(t){e.push({type:"error",stage:"plan",message:w(t)})}}isTurnAborted(e){return!0===e?.aborted||!0===e?.abortController.signal.aborted}execSessionIds(e){return[...e.execSessionIds].sort((e,t)=>e-t)}interruptExecSessions(e){const t=this.execSessionIds(e);0!==t.length&&this.sessionManager?.interrupt(t).catch(e=>{process.stderr.write(`roll chat: 中断后台会话失败: ${w(e)}\n`)})}abandonTurn(e){this.closed||this.activeTurn!==e||e.abortController.signal.aborted||(e.aborted=!0,e.cancellationReason??=p.runtime,this.gate.abortAll("本轮事件流已停止"),this.interruptExecSessions(e),e.abortController.abort(u))}cancellationMessage(e){const t=e.cancellationReason??p.runtime,s=this.execSessionIds(e),o=s.length>0?`本轮触达的后台会话 session_id: ${s.join(", ")}。`:"";switch(t){case p.user:return`已取消本轮;正在运行的模型或工具已收到中断请求。${o}已发生的外部副作用不会自动回滚。`;case p.timeout:return`本轮因运行超时${void 0!==this.turnTimeoutMs?`(${String(this.turnTimeoutMs)}ms)`:""}而中断;${s.length>0?`后台会话不会因轮超时被终止。${o}可在下一轮用 roll__exec_list 找回后继续轮询;`:""}未返回成功 tool result 或 Exit code: 0 的操作不能视为正常完成。`;case p.runtime:return`本轮被运行时中断;${o}未返回成功 tool result 或 Exit code: 0 的操作状态未知。`}}emitCancellation(e,t){if(t.cancellationEventEmitted)return;t.cancellationReason??=p.runtime,t.cancellationEventEmitted=!0;const s=this.execSessionIds(t);e.push({type:"turn-cancelled",reason:t.cancellationReason,message:this.cancellationMessage(t),...s.length>0?{execSessionIds:s}:{}})}persistCancelledTurn(e,t,s,o,i){if(this.closed)return this.messages.splice(s),t.cancellationPersisted=!0,void this.emitCancellation(e,t);t.cancellationPersisted||(this.messages.splice(s+1),this.messages.push(...$(o)),this.messages.push({role:"assistant",content:this.cancellationMessage(t)}),t.cancellationPersisted=!0,this.debug(e,"persist","persisting cancelled turn",i,{appendedMessages:this.messages.length-s}),this.onPersist?.(this.messages.slice(s))),this.emitCancellation(e,t)}cancel(){const e=this.activeTurn;return!!e&&(e.aborted=!0,e.cancellationReason=p.user,this.gate.abortAll("用户取消本轮"),this.interruptExecSessions(e),e.abortController.abort(m),!0)}close(){if(void 0!==this.closePromise)return this.closePromise;this.closed=!0;const e=this.activeTurn;return e&&(e.aborted=!0,e.cancellationReason??=p.runtime,this.gate.abortAll(),e.abortController.abort(u)),this.closePromise=(async()=>{const e=(this.sessionManager?.close()??Promise.resolve([])).then(e=>{for(const t of e)void 0!==t.cleanupError&&process.stderr.write(`roll chat: 后台会话 ${String(t.sessionId)} 清理失败: ${t.cleanupError}\n`)},e=>{process.stderr.write(`roll chat: 会话 ${this.id} 后台清理失败: ${w(e)}\n`)});await t(e,C)||process.stderr.write(`roll chat: 会话 ${this.id} 在 ${String(C)}ms 内未完成全部关闭步骤\n`);try{this.onClose?.()}catch(e){process.stderr.write(`roll chat: 会话 ${this.id} 关闭回调失败: ${w(e)}\n`)}})(),this.closePromise}abort(){this.close().catch(e=>{process.stderr.write(`roll chat: 会话 ${this.id} 关闭失败: ${w(e)}\n`)})}debug(e,t,s,o,i){this.debugEvents&&e.push({type:"debug",stage:t,message:s,...void 0!==o?{elapsedMs:Date.now()-o}:{},...void 0!==i?{data:i}:{}})}scheduleDebug(e,t,s,o,i){if(this.debugEvents)return setTimeout(()=>{this.debug(e,t,s,o,i)},5e3)}clearDebugTimer(e){void 0!==e&&clearTimeout(e)}}
1
+ import{createHash as e,randomUUID as t}from"node:crypto";import{waitForPromiseSettlement as s}from"../bounded-wait.js";import{stepCountIs as o,streamText as i,InvalidToolInputError as n}from"ai";import{buildAgentToolset as a}from"../tool-bridge/build-tools.js";import{buildAgentInstallToolset as r}from"../tool-bridge/agent-install-tool.js";import{buildSkillToolset as c}from"../tool-bridge/skill-tool.js";import{buildBashToolset as l}from"../tool-bridge/bash-tool.js";import{EXEC_COMMAND_NAME as u,EXEC_LIST_NAME as p,EXEC_POLL_NAME as h,buildSessionExecToolset as d}from"../tool-bridge/session-exec-tool.js";import{SessionManager as m}from"../bash/session/session-manager.js";import{isTerminalSessionState as g}from"../bash/session/types.js";import{withCleanEnv as v}from"../bash/clean-env.js";import{RUNTIME_CANCELLATION_ABORT_REASON as f,SESSION_CANCELLATION_REASONS as y,TURN_TIMEOUT_ABORT_REASON as C,USER_CANCELLATION_ABORT_REASON as b,createTurnCancellationMessage as k,isTurnTimeoutAbortReason as T,stripTurnCancellationMetadata as S}from"../types/cancellation.js";import{ToolRegistry as x}from"../tool-bridge/naming.js";import{TOOL_OUTCOME_KINDS as I,failedToolResult as E,isNormalizedToolResult as M,normalizeToolResult as w,readDisplayOutput as R,readToolOutcome as q}from"../tool-bridge/normalize-result.js";import{createToolExecutionRecord as P,prepareToolExecutionRecordForPersistence as A,redactSecretText as N,toRedactedToolExecutionRecordSummary as O}from"../tool-bridge/tool-execution-record.js";import{TOOL_RESOURCE_ACCESS_MODES as j,ToolExecutionCoordinator as $}from"../tool-bridge/tool-execution-coordinator.js";import{ApprovalGate as W}from"../approval/approval-gate.js";import{COMPACTION_DRAFT_FALLBACK_REASONS as D,compactMessages as L,CompactionDraftFallbackError as F,isCompactionSummaryAcknowledgement as V,readCompactionSummaryPayload as U}from"./compactor.js";import{AsyncEventQueue as z}from"./event-queue.js";import{applyExplicitSkillContext as G,attachExplicitSkillCheckpoint as J,prepareExplicitSkillContext as B,readExplicitSkillCheckpoint as H,stripExplicitSkillCheckpoints as _}from"./explicit-skill-context.js";import{createCancelledTurnRecoveryMessage as Y,materializeCancelledTurnRecoveryMessages as K,stripCancelledTurnRecoveryMessages as Q}from"./cancelled-turn-recovery.js";import{CAPABILITY_HOST_MODES as X,CAPABILITY_TOOL_ROLES as Z,buildEffectiveCapabilityTurnContext as ee,buildEffectiveCapabilityManifest as te,findCapabilityToolId as se}from"./capability-manifest.js";import{buildCapabilityTurnReminder as oe,buildChatSystemPromptFromManifest as ie}from"./system-prompt.js";import{buildCompactionCheckpointReminder as ne,buildCompactionToolState as ae,COMPACTION_CHECKPOINT_VERSION as re,createCompactionCheckpoint as ce,createCompactionSummary as le,createCheckpointSemanticReminderProjection as ue,findLatestRealUserGoal as pe,resolveActiveCompactionConstraints as he,TRANSCRIPT_MESSAGE_PROVENANCES as de}from"./compaction-checkpoint.js";import{buildCompactionSemanticModelContext as me,buildCompactionSemanticEvidenceRegistry as ge,mergeCompactionSemanticState as ve,replaceCompactionSemanticConstraints as fe,replaceCompactionSemanticGoal as ye,renderCompactionSemanticSummary as Ce,seedCompactionSemanticConstraints as be,seedLegacyCompactionSnapshotUncertainties as ke,validateCompactionModelDraft as Te}from"./compaction-semantic-state.js";import{ACTIVE_TOOL_PROTOCOL_REPAIR_STATUS as Se,repairActiveToolProtocol as xe}from"./tool-protocol-repair.js";import{buildTranscriptToolset as Ie}from"../tool-bridge/transcript-tool.js";const Ee=6e3,Me=1,we=1024,Re="AI_InvalidToolInputError:",qe={startsNewGoalScope:!1,goal:null,constraints:[],decisions:[],completedWork:[],pendingWork:[],resources:[],runningSessions:[],uncertainties:[],resolutions:[],evidenceReviews:[]};function Pe(t){const s=[...t];if(s.length<=we)return t;const o=`…#${e("sha256").update(t).digest("hex").slice(0,16)}`;return`${s.slice(0,we-[...o].length).join("")}${o}`}function Ae(){return{abortController:new AbortController,execSessionIds:new Set,pendingToolCalls:new Map,completedStepResponses:new Map,toolExecutions:[],hadPotentialSideEffects:!1,aborted:!1,cancellationEventEmitted:!1,cancellationPersistenceAttempted:!1,cancellationPersisted:!1}}function Ne(e,t){e.completedStepResponses.set(`${t.callId}:${String(t.stepNumber)}`,[...t.response.messages])}function Oe(e){return[...e.completedStepResponses.values()].flatMap(e=>[...e])}function je(e){return e instanceof Error?e.message:String(e)}function $e(e){const t=H(e.message),[s]=_([e.message]),o=s??e.message,i=void 0!==t&&"user"===o.role?t.snapshot.userPrompt:"string"==typeof o.content?o.content:JSON.stringify(o.content);return`message ${String(e.sequence)} ${o.role}: ${i}`}function We(e){return JSON.stringify(e)??String(e)}function De(e){const t=A(e);return{id:e.id,agentName:e.agentName,toolName:e.toolName,inputSummary:We(t.input.value),resultSummary:We(t.model),outcome:{kind:e.outcome.kind}}}function Le(e){return"user"===e.role&&"string"==typeof e.content&&void 0!==U(e.content)||"assistant"===e.role&&V(e.content)}function Fe(e,t){const s=t.flatMap((e,t)=>{if("user"===e.role&&"string"==typeof e.content){const t=U(e.content);if(void 0!==t)return 0===t.length?[]:[N(t)]}return"assistant"===e.role&&V(e.content)?[]:[N($e({sequence:t,provenance:de[1],createdAt:new Date(0).toISOString(),message:e}))]});return{checkpointId:e,messageFingerprints:t.map(e=>JSON.stringify(e)),fragments:s,removableRawMessageCount:t.filter(e=>!Le(e)).length}}function Ve(e,t,s){return!(1!==t?.version||t.id!==e.checkpointId||s.length<e.messageFingerprints.length)&&e.messageFingerprints.every((e,t)=>JSON.stringify(s[t])===e)}function Ue(e){return void 0===e?{messagesThroughSequence:-1,toolExecutionsThroughSequence:-1}:e.version===re?e.semanticEvidence:{messagesThroughSequence:e.transcript.messages.throughSequence,toolExecutionsThroughSequence:e.transcript.toolExecutions.throughSequence}}function ze(e,t){return t.aborted?Promise.reject(t.reason):new Promise((s,o)=>{let i=!1;const n=e=>{i||(i=!0,t.removeEventListener("abort",a),e())},a=()=>n(()=>o(t.reason));t.addEventListener("abort",a,{once:!0}),Promise.resolve(e).then(e=>n(()=>s(e)),e=>n(()=>o(e)))})}function Ge(e){return je(R(e))}function Je(e){return n.isInstance(e)||"string"==typeof e&&e.startsWith(Re)?I.invalidInput:I.toolFailed}function Be(){return({steps:e})=>e.at(-1)?.toolResults.some(e=>q(e.output).kind===I.userRejected)??!1}function He(e){return/context[_ -]?length|context window|maximum context|token limit|too many tokens|prompt is too long|input is too long/i.test(je(e))}function _e(e){const t=e.inputTokenDetails?.cacheReadTokens,s=e.inputTokenDetails?.cacheWriteTokens,o=e.outputTokenDetails?.reasoningTokens;return{...void 0!==e.inputTokens?{inputTokens:e.inputTokens}:{},...void 0!==e.outputTokens?{outputTokens:e.outputTokens}:{},...void 0!==e.totalTokens?{totalTokens:e.totalTokens}:{},...void 0!==t?{cachedInputTokens:t}:{},...void 0!==s?{cacheWriteTokens:s}:{},...void 0!==o?{reasoningTokens:o}:{}}}function Ye(e,t){return void 0===e&&void 0===t?void 0:(e??0)+(t??0)}function Ke(e,t){const s=Ye(e.cachedInputTokens,t.cachedInputTokens),o=Ye(e.cacheWriteTokens,t.cacheWriteTokens),i=Ye(e.reasoningTokens,t.reasoningTokens);return{inputTokens:(e.inputTokens??0)+(t.inputTokens??0),outputTokens:(e.outputTokens??0)+(t.outputTokens??0),totalTokens:(e.totalTokens??0)+(t.totalTokens??0),...void 0!==s?{cachedInputTokens:s}:{},...void 0!==o?{cacheWriteTokens:o}:{},...void 0!==i?{reasoningTokens:i}:{}}}function Qe(e,t){return void 0===t?e:void 0===e?t:Math.max(e,t)}function Xe(e,t){const s=e.trim(),o=t.trim();return s.length>0&&o.length>0&&o.startsWith(s)}function Ze(e,t){return void 0!==e?e:T(t)?y.timeout:y.runtime}function et(e){return e.flatMap(e=>{if("assistant"!==e.role||!Array.isArray(e.content))return[e];const t=e.content.filter(e=>"reasoning"!==e.type);return 0===t.length?[]:t.length===e.content.length?[e]:[{...e,content:t}]})}function tt(e,t,s){for(const o of Object.keys(t))e[o]=s}function st(e,t,s){const o={[u]:Z.sessionCommand,[h]:Z.sessionPoll,[p]:Z.sessionList};for(const i of Object.keys(t)){const t=s.resolve(i),n=t?o[t.toolName]:void 0;n&&(e[i]=n)}}function ot(e,t){const s=[...e];for(let e=s.length-1;e>=0;e-=1){const o=s[e];if("user"===o?.role){s[e]={...o,content:"string"==typeof o.content?`${t}\n\n${o.content}`:[{type:"text",text:t},...o.content]};break}}return s}export class AgentSession{id;model;maxSteps;messages;onPersist;onReplace;onToolExecution;listPersistedToolExecutions;getPersistedToolExecution;listPersistedTranscriptMessages;commitPersistedCompaction;inMemoryToolExecutions=[];inMemoryTranscript=[];legacyV1ActiveSnapshot;compactionResources=new Map;contextWindow;compaction;turnTimeoutMs;onClose;providerOptions;structuredOutputProviderOptions;structuredOutputReasoning;onProviderOptionsChange;debugEvents;policy;systemPrompt;explicitSystemPrompt;capabilityContext;resolveDynamicCapabilityContext;capabilityManifest;lastCapabilityTurnContext;toolRoles;skillSummaries;skillLibrary;skillToolBuilt=!1;toolSourceAgentNames;gate=new W;toolCoordinator=new $;tools;registry;sessionManager;sessionManagerInstanceId=t();compactionCheckpoint;emit;activeTurn;closePromise;closed=!1;sessionUsage={inputTokens:0,outputTokens:0,totalTokens:0};lastInputTokens;needsCompaction=!1;constructor(e){this.id=e.id,this.model=e.model,this.maxSteps=e.maxSteps;const t=e.initialMessages?et(e.initialMessages):[];this.messages=[...xe(t).messages],this.legacyV1ActiveSnapshot=1===e.initialCheckpoint?.version?Fe(e.initialCheckpoint.id,this.messages):void 0,this.onPersist=e.onPersist,this.onReplace=e.onReplace,this.onToolExecution=e.onToolExecution,this.listPersistedToolExecutions=e.listToolExecutions,this.getPersistedToolExecution=e.getToolExecution,this.listPersistedTranscriptMessages=e.listTranscriptMessages,this.commitPersistedCompaction=e.commitCompaction,this.compactionCheckpoint=e.initialCheckpoint;let s=Ue(e.initialCheckpoint).messagesThroughSequence+1;for(const e of this.messages)Le(e)||(this.inMemoryTranscript.push({sequence:s,provenance:de[0],createdAt:new Date(0).toISOString(),message:e}),s+=1);for(const t of e.initialCheckpoint?.resources??[])this.retainCompactionResource(t);this.contextWindow=e.contextWindow,this.compaction=e.compaction,this.turnTimeoutMs=e.turnTimeoutMs,this.onClose=e.onClose,this.providerOptions=e.providerOptions,this.structuredOutputProviderOptions=e.structuredOutputProviderOptions,this.structuredOutputReasoning=e.structuredOutputReasoning,this.onProviderOptionsChange=e.onProviderOptionsChange,this.debugEvents=e.debugEvents??!1,this.policy=e.policy,this.explicitSystemPrompt=e.systemPrompt,this.resolveDynamicCapabilityContext=e.resolveDynamicCapabilityContext,this.skillLibrary=e.skillLibrary,this.skillSummaries=e.skillLibrary?.list()??[],this.toolSourceAgentNames=new Set(e.sources.map(e=>e.agentName));const o=new x,i={},n=e.readCheckpointTranscript?Ie(e.readCheckpointTranscript,o,this.toolCoordinator,`thread:${this.id}:transcript`):{};tt(i,n,Z.transcriptRead);const c=e.skillLibrary?this.buildSkillTools(o):{};tt(i,c,Z.skill);const u={...e.policy?{policy:e.policy}:{},requestApproval:e=>this.requestApproval(e),emitEvent:e=>this.emit?.(e),coordinator:this.toolCoordinator},p=e.bashClassifier?{classifier:e.bashClassifier}:{},h=e.bash?l(e.bash,o,u,p):{};tt(i,h,Z.shell),e.bashSession&&(this.sessionManager=new m({maxSessions:e.bashSession.maxSessions,profile:e.bashSession.profile,env:v(e.bashSession.env??process.env),bufferCapacity:e.bashSession.bufferCapacity}));const g=e.bashSession&&this.sessionManager?d({workdir:e.bashSession.workdir,defaultYieldMs:e.bashSession.defaultYieldMs,maxOutputTokens:e.bashSession.maxOutputTokens,...e.bashSession.env?{env:e.bashSession.env}:{}},this.sessionManager,o,u,{...p,onSessionTouched:e=>this.activeTurn?.execSessionIds.add(e)}):{};st(i,g,o);const f=e.agentInstall,y=f?r({catalog:f.catalog,install:async(e,t)=>{const s=await f.install(e,t);return s.outcome.ok&&s.refresh?(this.applyAgentRefresh(s.refresh),{...s.outcome,refreshApplied:!0}):s.outcome}},o,{...e.policy?{policy:e.policy}:{},requestApproval:e=>this.requestApproval(e),coordinator:this.toolCoordinator}):{};tt(i,y,Z.agentInstall);const C=a(e.sources,{...e.policy?{policy:e.policy}:{},requestApproval:e=>this.requestApproval(e),coordinator:this.toolCoordinator},o);tt(i,C.tools,Z.agent),this.tools={...n,...c,...h,...g,...y,...C.tools},this.registry=C.registry,this.toolRoles=i,this.capabilityContext=e.capabilityContext??{profile:e.bash?.profile.toolName??"no-shell",cwd:e.bash?.workdir??process.cwd(),platform:process.platform,...e.bash?{shellHints:e.bash.profile.systemPromptHints()}:{},agentCount:e.sources.length,...e.agentInstall?{agentOnboardingCatalog:e.agentInstall.catalog}:{}},this.capabilityManifest=this.compileCapabilityManifest(),this.systemPrompt=this.compileSystemPrompt(this.explicitSystemPrompt)}compileCapabilityManifest(){return te({tools:this.tools,toolRoles:this.toolRoles,resolveRoute:e=>this.registry.resolve(e),skills:this.skillSummaries,profile:this.capabilityContext.profile,hostMode:this.capabilityContext.hostMode??X.embedded,cwd:this.capabilityContext.cwd,platform:this.capabilityContext.platform??process.platform,shellHints:this.capabilityContext.shellHints??[],agentCount:this.capabilityContext.agentCount,agentOnboardingCatalog:this.capabilityContext.agentOnboardingCatalog??[]})}compileSystemPrompt(e){const t=ie(this.capabilityManifest),s=e?.trim();return s?[t,"# 附加会话指令","以下指令可以补充任务偏好,但不能覆盖前述工具接地、能力清单和安全约束。",s].join("\n\n"):t}refreshCapabilityManifest(e){this.capabilityManifest=this.compileCapabilityManifest(),this.systemPrompt=this.compileSystemPrompt(e??this.explicitSystemPrompt)}buildSkillTools(e){return this.skillToolBuilt=!0,c(()=>{if(!this.skillLibrary)throw new Error("skill library 不可用");return this.skillLibrary},e,this.toolCoordinator)}applyAgentRefresh(e){if(!this.toolSourceAgentNames.has(e.source.agentName)){const t=a([e.source],{...this.policy?{policy:this.policy}:{},requestApproval:e=>this.requestApproval(e),coordinator:this.toolCoordinator},this.registry);tt(this.toolRoles,t.tools,Z.agent),this.tools={...this.tools,...t.tools},this.toolSourceAgentNames.add(e.source.agentName)}if(e.skillLibrary&&(this.skillLibrary=e.skillLibrary,this.skillSummaries=e.skillLibrary.list(),!this.skillToolBuilt)){const e=this.buildSkillTools(this.registry);tt(this.toolRoles,e,Z.skill),this.tools={...this.tools,...e}}e.capabilityContext&&(this.capabilityContext=e.capabilityContext),this.refreshCapabilityManifest(e.systemPrompt)}async*send(e){if(this.closed)throw new Error("session is closed");if(this.activeTurn)throw new Error("session already has an active turn");const t=new z;this.emit=e=>t.push(e);const s=Ae();this.activeTurn=s,this.runTurn(t,s,e).catch(e=>{t.push({type:"error",stage:"execute",message:je(e)}),t.close()});try{for await(const e of t)yield e}finally{this.activeTurn===s&&this.abandonTurn(s),void 0!==this.emit&&(this.emit=void 0)}}async*compact(e="manual"){if(this.closed)throw new Error("session is closed");if(this.activeTurn)throw new Error("session already has an active turn");const t=new z;this.emit=e=>t.push(e);const s=Ae();this.activeTurn=s,this.runCompactionTurn(t,s,e).catch(e=>{t.push({type:"error",stage:"plan",message:je(e)}),t.close()});try{for await(const e of t)yield e}finally{this.activeTurn===s&&this.abandonTurn(s),void 0!==this.emit&&(this.emit=void 0)}}async runTurn(e,s,n){const a=Date.now();let r,c;try{if(this.debug(e,"turn","start",a,{messages:this.messages.length,tools:Object.keys(this.tools).length,maxSteps:this.maxSteps,...void 0!==this.contextWindow?{contextWindow:this.contextWindow}:{},...void 0!==this.lastInputTokens?{lastInputTokens:this.lastInputTokens}:{}}),this.shouldAutoCompact()){this.debug(e,"compaction","auto requested before turn",a,{messages:this.messages.length,...void 0!==this.lastInputTokens?{lastInputTokens:this.lastInputTokens}:{}});try{await this.runCompaction(e,"auto",s)}catch(t){return void e.push({type:"error",stage:"plan",message:je(t)})}}if(s.aborted||s.abortController.signal.aborted)return r=this.messages.length,this.messages.push({role:"user",content:n}),void this.persistCancelledTurn(e,s,r,[],a);e.push({type:"message-start",messageId:t()}),void 0!==this.turnTimeoutMs&&(c=setTimeout(()=>{s.abortController.signal.aborted||(this.captureCancellationActivity(s),s.aborted=!0,s.cancellationReason=y.timeout,this.gate.abortAll("本轮运行超时"),s.abortController.abort(C))},this.turnTimeoutMs));let l=0;const u=B({rawInput:n,skillSummaries:this.skillSummaries,skillLibrary:this.skillLibrary}),p={role:"user",content:n},h=u.skillNames.length>0?J(p,u):p;let d={};try{d=await ze(this.resolveDynamicCapabilityContext?.(s.abortController.signal)??{},s.abortController.signal)??{}}catch(t){if(this.isTurnAborted(s))return r=this.messages.length,this.messages.push(h),s.cancellationReason=Ze(s.cancellationReason,s.abortController.signal.reason??t),void this.persistCancelledTurn(e,s,r,[],a);this.debug(e,"turn","dynamic capability context unavailable",a,{message:je(t)})}const m=ee(this.capabilityManifest,{explicitSkillNames:u.skillNames,...d.ruleIds?{ruleIds:d.ruleIds}:{},...d.vcs?{vcs:d.vcs}:{},sessions:(this.sessionManager?.list()??[]).map(e=>({sessionId:e.sessionId,state:e.state}))});this.lastCapabilityTurnContext=m;const g=oe(m);for(;;){r=this.messages.length,this.messages.push(h);const t=se(this.capabilityManifest,Z.transcriptRead),n=this.compactionCheckpoint?ne(this.compactionCheckpoint,this.sessionManagerInstanceId,t):void 0,c=_(S(K(this.messages))),p=ot(u.skillNames.length>0?G(c,u):c,n?`${g}\n\n${n}`:g);this.debug(e,"model","calling streamText",a,{messages:this.messages.length,tools:Object.keys(this.tools).length,contextRecoveryAttempts:l,...void 0!==this.turnTimeoutMs?{timeoutMs:this.turnTimeoutMs}:{}});let d=[];const m=()=>i({model:this.model,system:this.systemPrompt,messages:p,tools:this.tools,stopWhen:[o(this.maxSteps),Be()],toolApproval:async({toolCall:e})=>{await this.toolCoordinator.prepare(e.toolCallId,e.toolName,e.input)},onLanguageModelCallStart:({callId:e})=>this.toolCoordinator.startBatch(e),onLanguageModelCallEnd:({callId:e,content:t})=>this.toolCoordinator.sealBatch(e,t.flatMap(e=>"tool-call"!==e.type||!0===e.providerExecuted||"invalid"in e&&!0===e.invalid?[]:[{toolCallId:e.toolCallId,toolId:e.toolName}])),abortSignal:s.abortController.signal,...this.providerOptions?{providerOptions:this.providerOptions}:{},onError:()=>{},onStepEnd:e=>{Ne(s,e),d=Oe(s)},onAbort:({steps:e})=>{for(const t of e)Ne(s,t);d=Oe(s)}});let v;try{v=m()}catch(t){if(this.isTurnAborted(s)||T(t))return s.aborted=!0,s.cancellationReason=Ze(s.cancellationReason,t),void this.persistCancelledTurn(e,s,r,[],a);if(!He(t))throw t;this.needsCompaction=!0,this.messages.splice(r);const o=l<1,i=!!o&&await this.recoverFromContextError(e,s);if(this.isTurnAborted(s))return r=this.messages.length,this.messages.push(h),void this.persistCancelledTurn(e,s,r,[],a);if(i&&o){l+=1;continue}return this.persistContextFailure(e,h,!1,!1,a),void e.push({type:"error",stage:"execute",message:je(t)})}this.debug(e,"model","streamText returned",a);let f,C,b,k,x,R,q="",A="",N=!1,O=0,j=!1,$=!1;const W=this.scheduleDebug(e,"model","waiting for first stream event",a,{messages:this.messages.length});try{for await(const t of v.fullStream){if(this.closed){s.aborted=!0,s.cancellationReason??=y.runtime;break}switch($||($=!0,this.clearDebugTimer(W),this.debug(e,"model","first stream event",a,{part:t.type})),t.type){case"reasoning-start":e.push({type:"reasoning-start",reasoningId:t.id});break;case"reasoning-delta":e.push({type:"reasoning-delta",reasoningId:t.id,delta:t.text});break;case"reasoning-end":e.push({type:"reasoning-end",reasoningId:t.id});break;case"text-delta":if(!N){const s=A+t.text;if(Xe(s,u.userPrompt)){A=s;break}if(A.length>0){const s=A+t.text;A="",q+=s,e.push({type:"text-delta",delta:s});break}}q+=t.text,e.push({type:"text-delta",delta:t.text});break;case"tool-call":{N=!0,A="";const o=this.registry.resolve(t.toolName);s.pendingToolCalls.set(t.toolCallId,{toolCallId:t.toolCallId,agentName:o?.agentName??t.toolName,toolName:o?.toolName??t.toolName,input:t.input,potentialSideEffect:!0!==o?.annotations?.readOnlyHint||!0===o.annotations.destructiveHint,resources:this.toolCoordinator.describeResources(t.toolName,t.input)}),e.push({type:"tool-call",toolCallId:t.toolCallId,agentName:o?.agentName??t.toolName,toolName:o?.toolName??t.toolName,input:t.input});break}case"tool-result":{const o=this.registry.resolve(t.toolName),i=s.pendingToolCalls.get(t.toolCallId),n=M(t.output)?t.output:w(t.output),a=n.outcome,r=n.display,c=P({toolCallId:t.toolCallId,agentName:o?.agentName??t.toolName,toolName:o?.toolName??t.toolName,input:i?.input,result:n});this.persistToolExecution(c,s),!0===i?.potentialSideEffect&&a.kind!==I.userRejected&&a.kind!==I.policyDenied&&a.kind!==I.invalidInput&&(s.hadPotentialSideEffects=!0),a.kind===I.success&&void 0!==i&&this.rememberCompactionResources(c,i.resources),s.pendingToolCalls.delete(t.toolCallId),e.push({type:"tool-result",toolCallId:t.toolCallId,agentName:o?.agentName??t.toolName,toolName:o?.toolName??t.toolName,executionId:c.id,outcome:a,display:r,output:r,isError:a.kind!==I.success}),s.aborted||s.abortController.signal.aborted||a.kind!==I.userRejected||(R=Ge(t.output));break}case"tool-error":{const o=this.registry.resolve(t.toolName),i=Ge(t.error),n=E(Je(t.error),i,{raw:t.error}),a=P({toolCallId:t.toolCallId,agentName:o?.agentName??t.toolName,toolName:o?.toolName??t.toolName,input:s.pendingToolCalls.get(t.toolCallId)?.input,result:n});this.persistToolExecution(a,s),!0===s.pendingToolCalls.get(t.toolCallId)?.potentialSideEffect&&n.outcome.kind!==I.invalidInput&&(s.hadPotentialSideEffects=!0),s.pendingToolCalls.delete(t.toolCallId),e.push({type:"tool-result",toolCallId:t.toolCallId,agentName:o?.agentName??t.toolName,toolName:o?.toolName??t.toolName,executionId:a.id,outcome:n.outcome,display:n.display,output:i,isError:!0});break}case"finish-step":{const s=_e(t.usage);C=Qe(C,s.inputTokens),O+=1,k=t.finishReason;const o=t.performance.outputTokensPerSecond??t.performance.effectiveOutputTokensPerSecond;void 0!==o&&Number.isFinite(o)&&(b=o),e.push({type:"step-finish",finishReason:t.finishReason,usage:s});break}case"finish":!N&&A.length>0&&(q+=A,e.push({type:"text-delta",delta:A}),A=""),f=_e(t.totalUsage);break;case"error":if(this.isTurnAborted(s)||T(t.error)){s.aborted=!0,s.cancellationReason=Ze(s.cancellationReason,t.error);break}j=He(t.error),j&&(this.needsCompaction=!0),x=je(t.error);break;case"abort":s.aborted=!0,s.cancellationReason=Ze(s.cancellationReason,t.reason)}}}catch(e){this.isTurnAborted(s)||T(e)?(s.aborted=!0,s.cancellationReason=Ze(s.cancellationReason,e)):(j=He(e),j&&(this.needsCompaction=!0),x=je(e))}finally{this.clearDebugTimer(W)}if(this.debug(e,"model","fullStream finished",a,{textChars:q.length}),void 0!==x){if(this.messages.splice(r),j){const t=0===q.length&&!N,o=t&&l<1,i=!(!o&&t)&&await this.recoverFromContextError(e,s);if(this.isTurnAborted(s))return r=this.messages.length,this.messages.push(h),void this.persistCancelledTurn(e,s,r,[],a);if(t&&i&&o){l+=1;continue}const n=t?"":";本次尝试已有内容或操作开始执行,为避免重复执行,未自动重试";return this.persistContextFailure(e,h,q.length>0,N,a),void e.push({type:"error",stage:"execute",message:`${x}${n}`})}return void e.push({type:"error",stage:"execute",message:x})}if(s.aborted||s.abortController.signal.aborted)return s.cancellationReason=Ze(s.cancellationReason,s.abortController.signal.reason),void this.persistCancelledTurn(e,s,r,d,a);let D;this.debug(e,"model","awaiting response messages",a);const L=this.scheduleDebug(e,"model","still awaiting response messages",a);try{D=(await v.steps).flatMap(e=>e.response.messages)}catch(t){if(this.clearDebugTimer(L),this.isTurnAborted(s)||T(t))return s.aborted=!0,s.cancellationReason=Ze(s.cancellationReason,t),void this.persistCancelledTurn(e,s,r,d,a);const o=He(t);if(o&&(this.needsCompaction=!0),this.messages.splice(r),o){const o=0===q.length&&!N,i=o&&l<1,n=!(!i&&o)&&await this.recoverFromContextError(e,s);if(this.isTurnAborted(s))return r=this.messages.length,this.messages.push(h),void this.persistCancelledTurn(e,s,r,[],a);if(o&&n&&i){l+=1;continue}const c=o?"":";本次尝试已有内容或操作开始执行,为避免重复执行,未自动重试";return this.persistContextFailure(e,h,q.length>0,N,a),void e.push({type:"error",stage:"execute",message:`${je(t)}${c}`})}return void e.push({type:"error",stage:"execute",message:je(t)})}finally{this.clearDebugTimer(L)}if(this.debug(e,"model","response messages ready",a,{responseMessages:D.length}),this.isTurnAborted(s))return s.cancellationReason=Ze(s.cancellationReason,s.abortController.signal.reason),void this.persistCancelledTurn(e,s,r,d,a);const F=et(D);if(this.messages.push(...F),void 0!==R){const t=0===q.length?R:`\n${R}`;q+=t,e.push({type:"text-delta",delta:t}),this.messages.push({role:"assistant",content:R})}this.debug(e,"persist","persisting messages",a,{appendedMessages:this.messages.length-r}),this.persistMessages(this.messages.slice(r)),this.debug(e,"persist","messages persisted",a,{totalMessages:this.messages.length}),f&&(this.sessionUsage=Ke(this.sessionUsage,f));const V=C??f?.inputTokens;void 0!==V&&(this.lastInputTokens=V);const U=O>=this.maxSteps&&"tool-calls"===k;return void e.push({type:"message-finish",text:q,...f?{totalUsage:f}:{},sessionUsage:{...this.sessionUsage},...void 0!==C?{contextInputTokens:C}:{},...void 0!==b?{outputTokensPerSecond:b}:{},...U?{stoppedAtStepLimit:!0}:{}})}}catch(t){if(this.isTurnAborted(s)||T(t))return s.aborted=!0,s.cancellationReason=Ze(s.cancellationReason,s.abortController.signal.reason??t),void(void 0!==r?this.persistCancelledTurn(e,s,r,[],a):this.emitCancellation(e,s));void 0!==r&&this.messages.splice(r),this.debug(e,"turn","error",a,{message:je(t)}),e.push({type:"error",stage:"execute",message:je(t)})}finally{void 0!==c&&clearTimeout(c),this.toolCoordinator.finishTurn(),this.activeTurn===s&&(this.activeTurn=void 0),e.close()}}persistMessages(e){if(this.onPersist?.(e),void 0!==this.listPersistedTranscriptMessages)return;let t=(this.inMemoryTranscript.at(-1)?.sequence??-1)+1;const s=(new Date).toISOString();for(const o of e)Le(o)||(this.inMemoryTranscript.push({sequence:t,provenance:de[0],createdAt:s,message:o}),t+=1)}persistContextFailure(e,t,s,o,i){const n=["本轮因上下文窗口溢出而中断,未自动重放。"];s&&n.push("本轮已产生部分文本,持久历史仅保留此中断标记。"),o&&n.push("本轮已有操作开始执行,部分结果可能已经生效且不会自动撤销,请先检查实际结果。");const a=this.messages.length;this.messages.push(t),this.messages.push({role:"assistant",content:n.join("")}),this.debug(e,"persist","persisting context-overflow marker",i,{hadToolActivity:o,producedText:s});try{this.persistMessages(this.messages.slice(a))}catch(t){this.messages.splice(a),e.push({type:"error",stage:"execute",message:`上下文溢出记录持久化失败: ${je(t)}`})}}persistToolExecution(e,t){this.onToolExecution?.(e),this.inMemoryToolExecutions.push(e),t.toolExecutions.push(e)}rememberCompactionResources(e,t){for(const s of t){const t={key:Pe(s.key),mode:s.mode,evidenceToolCallId:e.toolCallId,evidenceExecutionId:e.id};this.retainCompactionResource(t)}}retainCompactionResource(e){const t=this.compactionResources.get(e.key),s=void 0===t||e.mode===j.write||t.mode!==j.write?e:t;this.compactionResources.delete(e.key),this.compactionResources.set(e.key,s)}requestApproval(e){const s=t(),o=this.gate.request(s);return void 0===this.emit?(this.gate.resolve(s,{approved:!1,reason:"approval event could not be delivered"}),o):(this.emit({type:"confirmation-required",approvalId:s,agentName:e.agentName,toolName:e.toolName,input:e.input,...e.reason?{reason:e.reason}:{}}),o)}approve(e){return this.gate.resolve(e,{approved:!0})}reject(e,t){return this.gate.resolve(e,{approved:!1,...t?{reason:t}:{}})}getMessages(){return _(Q(this.messages))}getToolExecutions(e={},t=!1){const s=this.listPersistedToolExecutions?this.listPersistedToolExecutions(e):this.inMemoryToolExecutions.map((e,t)=>({...e,sequence:t})).filter(t=>t.sequence>(e.afterSequence??-1)&&(void 0===e.toolCallId||t.toolCallId===e.toolCallId)).slice(0,e.limit??100);return t?s:s.map(e=>({...O(e),sequence:e.sequence}))}getToolExecution(e,t=!1){const s=this.getPersistedToolExecution?.(e),o=s??(()=>{const t=this.inMemoryToolExecutions.findIndex(t=>t.id===e),s=this.inMemoryToolExecutions[t];return s?{...s,sequence:t}:void 0})();return!o||t?o:{...O(o),sequence:o.sequence}}getCapabilityManifest(){return structuredClone(this.capabilityManifest)}getCapabilityTurnContext(){return this.lastCapabilityTurnContext?structuredClone(this.lastCapabilityTurnContext):void 0}getContextWindow(){return this.contextWindow}getSessionUsage(){return{...this.sessionUsage}}getSkillSummaries(){return this.skillSummaries}setProviderOptions(e){this.providerOptions=e,this.onProviderOptionsChange?.(e)}shouldAutoCompact(){const e=this.compaction;return void 0!==e&&e.enabled&&(this.needsCompaction||void 0!==this.contextWindow&&void 0!==this.lastInputTokens&&this.lastInputTokens/this.contextWindow>=e.threshold)}listCompactionTranscriptEvidence(){const e=this.listPersistedTranscriptMessages;if(void 0===e)return[...this.inMemoryTranscript];const t=[];let s=-1;for(;;){const o=e({afterSequence:s,limit:500});t.push(...o);const i=o.at(-1)?.sequence;if(o.length<500||void 0===i||i<=s)break;s=i}return t}listCompactionToolEvidence(){const e=this.listPersistedToolExecutions;if(void 0===e)return this.inMemoryToolExecutions.map((e,t)=>({...e,sequence:t}));const t=[];let s=-1;for(;;){const o=e({afterSequence:s,limit:500});t.push(...o);const i=o.at(-1)?.sequence;if(o.length<500||void 0===i||i<=s)break;s=i}return t}latestExplicitSkillNames(e,t){if(void 0===t)return this.compactionCheckpoint?.context.explicitSkillNames??[];const s=e.find(e=>e.sequence===t);return void 0===s?[]:H(s.message)?.snapshot.skillNames??[]}currentCompactionRunningWork(){const e=(this.compactionCheckpoint?.runningWork??[]).filter(e=>e.managerInstanceId!==this.sessionManagerInstanceId).map(e=>({...e,recoverability:"live"===e.recoverability?"stale":e.recoverability})),t=(new Date).toISOString();return[...e,...(this.sessionManager?.list()??[]).map(e=>({managerInstanceId:this.sessionManagerInstanceId,sessionId:e.sessionId,state:e.state,recoverability:g(e.state)?"unavailable":"live",commandPreview:e.commandPreview,workdir:e.workdir,observedAt:t,wallTimeMs:e.wallTimeMs,...void 0!==e.exitCode?{exitCode:e.exitCode}:{},...void 0!==e.terminationCause?{terminationCause:e.terminationCause}:{},...void 0!==e.cleanupError?{cleanupError:e.cleanupError}:{}}))].slice(-128)}captureCompactionEvidence(){const e=this.listCompactionTranscriptEvidence(),t=this.listCompactionToolEvidence(),s=[...this.compactionResources.values()].slice(-256),o=[...this.currentCompactionRunningWork()],i="legacy_snapshot"===this.compactionCheckpoint?.transcript.completeness||e.some(e=>"legacy_snapshot"===e.provenance)?"legacy_snapshot":"complete",n=this.compactionCheckpoint?.goal,a=pe(e),r=a??n,c=void 0!==a&&(void 0===n||a.sourceSequence>n.sourceSequence)?a.sourceSequence:void 0,l=this.compactionCheckpoint?.version===re?this.compactionCheckpoint.semanticState:void 0,u=be(l,this.compactionCheckpoint?.constraints??[]),p=1===this.compactionCheckpoint?.version,h=void 0!==this.legacyV1ActiveSnapshot&&Ve(this.legacyV1ActiveSnapshot,this.compactionCheckpoint,this.messages),d=p?h&&void 0!==this.legacyV1ActiveSnapshot?ke(u,{checkpointId:this.legacyV1ActiveSnapshot.checkpointId,fragments:this.legacyV1ActiveSnapshot.fragments}):{state:u,requiredItemIds:[],transcriptFragments:[],complete:!1}:{state:u,requiredItemIds:[],transcriptFragments:[],complete:!0},m=d.state,g=Ue(this.compactionCheckpoint),v=g.messagesThroughSequence,f=g.toolExecutionsThroughSequence,y=e.filter(e=>e.sequence>v),C=new Set(y.filter(e=>e.provenance===de[1]&&Le(e.message)).map(e=>e.sequence)),b=void 0===r?void 0:e.find(e=>e.sequence===r.sourceSequence),k=ge({messages:[...y.filter(e=>!C.has(e.sequence)),...void 0===b||y.includes(b)?[]:[b]].map(e=>({sequence:e.sequence,role:e.message.role,summary:$e(e)})),toolExecutions:t.filter(e=>e.sequence>f).map(De),resources:s.slice().reverse().map(e=>({key:e.key,mode:e.mode})),runningSessions:[]}),T=me(k,m,48e3);return{transcript:e,toolExecutions:t,resources:s,runningWork:o,completeness:i,goal:r,newGoalSourceSequence:c,previousSemanticState:m,registry:k,modelContext:T.prompt,presentedEvidenceIds:T.includedEvidenceIds,expectedActiveMessages:[...this.messages],expectedLatestCheckpointId:this.compactionCheckpoint?.id,previousSemanticEvidenceWatermarks:g,autoCoveredMessageSequences:C,legacySnapshotMigrationComplete:d.complete,legacySnapshotRequiredItemIds:d.requiredItemIds,legacySnapshotTranscriptFragments:d.transcriptFragments,legacySnapshotRemovableRawMessageCount:d.complete&&h?this.legacyV1ActiveSnapshot?.removableRawMessageCount??0:0,evidenceWatermarks:{transcriptMessagesThroughSequence:Math.max(this.compactionCheckpoint?.transcript.messages.throughSequence??-1,e.at(-1)?.sequence??-1),toolExecutionsThroughSequence:Math.max(this.compactionCheckpoint?.transcript.toolExecutions.throughSequence??-1,t.at(-1)?.sequence??-1)}}}buildCompactionDraft(t,s,o,i){const n=Te({draft:s??qe,evidenceRegistry:t.registry,presentedEvidenceIds:t.presentedEvidenceIds,...void 0!==t.previousSemanticState?{previousState:t.previousSemanticState}:{},...void 0!==t.goal?{harnessGoal:{verbatimRequest:t.goal.verbatimRequest,sourceSequence:t.goal.sourceSequence}}:{}});let a=ve(t.previousSemanticState,n,{startsNewGoalScope:n.startsNewGoalScope});if(void 0===s){const e=he(t.transcript,this.compactionCheckpoint?.constraints??[]),s=JSON.stringify(e)!==JSON.stringify(this.compactionCheckpoint?.constraints??[]);a=fe(a,e);const o=void 0===this.compactionCheckpoint?.goal?t.goal:void 0===t.goal||void 0===t.newGoalSourceSequence||s?this.compactionCheckpoint.goal:t.goal;a=ye(a,o)}const r=a.constraints.flatMap(e=>{const t=e.provenance.find(e=>"message"===e.kind)?.messageSequence;return null==t?[]:[{quote:e.text,sourceSequence:t}]}),c=a.goal?.provenance.find(e=>"message"===e.kind)?.messageSequence,l=(null==c?void 0:pe(t.transcript.filter(e=>e.sequence===c)))??this.compactionCheckpoint?.goal??t.goal,u=void 0!==l&&l.sourceSequence!==this.compactionCheckpoint?.goal?.sourceSequence?l.sourceSequence:void 0,p=o?Ce(a):void 0,h=void 0!==p?le(p):void 0!==i?{status:"fallback",reason:i}:{status:"skipped"},d=new Set(ue(a).items.map(e=>e.itemId)),m=0===t.legacySnapshotRequiredItemIds.length||t.legacySnapshotRequiredItemIds.every(e=>d.has(e)),g=ae(t.transcript.map(e=>e.message),t.toolExecutions,32,t.completeness);return{draft:{...l?{goal:l}:{},constraints:[...r],resources:[...t.resources],toolState:g,runningWork:[...t.runningWork],context:{cwd:this.capabilityManifest.dynamicContext.cwd,stableRuleIds:[...this.capabilityManifest.stableContext.rules],systemPromptSha256:e("sha256").update(this.systemPrompt).digest("hex"),skills:this.capabilityManifest.skills.map(e=>({name:e.name,source:e.source})),explicitSkillNames:[...this.latestExplicitSkillNames(t.transcript,u)]},summary:h},semanticState:a,semanticSummaryText:p,semanticRejectionCount:n.rejections.length,legacySnapshotReminderCoverageComplete:m,coveredEvidenceIds:n.coveredEvidenceIds,expectedActiveMessages:t.expectedActiveMessages,expectedLatestCheckpointId:t.expectedLatestCheckpointId,evidenceWatermarks:t.evidenceWatermarks,legacySnapshotTranscriptFragments:t.legacySnapshotTranscriptFragments}}async runCompactionTurn(e,t,s){try{await this.runCompaction(e,s,t,!0)}catch(t){e.push({type:"error",stage:"plan",message:je(t)})}finally{this.activeTurn===t&&(this.activeTurn=void 0),e.close()}}advanceCompactionSemanticEvidenceWatermarks(e,t){const s=e.previousSemanticEvidenceWatermarks;if(0===t.length)return s;const o=new Set(t.filter(t=>e.presentedEvidenceIds.includes(t))),i=new Map(e.registry.flatMap(e=>"message"===e.provenance.kind&&null!==e.provenance.messageSequence?[[e.provenance.messageSequence,e.evidenceId]]:[]));let n=s.messagesThroughSequence;for(let t=s.messagesThroughSequence+1;;t+=1){if(e.autoCoveredMessageSequences.has(t)){n=t;continue}const s=i.get(t);if(void 0===s||!o.has(s))break;n=t}const a=new Map(e.registry.flatMap(e=>"tool_execution"===e.provenance.kind&&null!==e.provenance.toolExecutionId?[[e.provenance.toolExecutionId,e.evidenceId]]:[])),r=new Map(e.toolExecutions.map(e=>[e.sequence,e]));let c=s.toolExecutionsThroughSequence;for(let e=s.toolExecutionsThroughSequence+1;;e+=1){const t=r.get(e);if(void 0===t)break;const s=a.get(t.id);if(void 0===s||!o.has(s))break;c=e}return{messagesThroughSequence:n,toolExecutionsThroughSequence:c}}countContiguousPresentedMessageEvidence(e){if(!e.legacySnapshotMigrationComplete)return 0;const t=new Set(e.presentedEvidenceIds),s=new Map(e.registry.flatMap(e=>"message"===e.provenance.kind&&null!==e.provenance.messageSequence?[[e.provenance.messageSequence,e.evidenceId]]:[]));let o=e.legacySnapshotRemovableRawMessageCount;for(let i=e.previousSemanticEvidenceWatermarks.messagesThroughSequence+1;;i+=1){if(e.autoCoveredMessageSequences.has(i))continue;const n=s.get(i);if(void 0===n||!t.has(n))break;o+=1}return o}async runCompaction(e,t,s,o=!1){const i=Date.now(),n=this.compaction,a=n?.strategy??"summarize";if(this.debug(e,"compaction","start",i,{reason:t,messages:this.messages.length,timeoutMs:n?.timeoutMs??12e4,maxOutputTokens:n?.maxOutputTokens??8192}),e.push({type:"compaction-start",reason:t}),this.isTurnAborted(s))return o&&void 0!==s&&this.emitCancellation(e,s),!1;if(!n)return e.push({type:"context-compacted",reason:t,strategy:a,removed:0,kept:this.messages.length}),!1;const r=this.lastInputTokens;let c,l=n.strategy;const u=s?.abortController.signal,p=Date.now(),h=this.captureCompactionEvidence();if(this.debug(e,"compaction","evidence ready",p,{transcriptMessages:h.transcript.length,toolExecutions:h.toolExecutions.length}),1===this.compactionCheckpoint?.version&&void 0===this.commitPersistedCompaction)return this.debug(e,"compaction","legacy V1 snapshot requires durable transcript",i),e.push({type:"context-compacted",reason:t,strategy:l,removed:0,kept:this.messages.length}),!1;if(!h.legacySnapshotMigrationComplete)return this.debug(e,"compaction","legacy V1 snapshot migration incomplete",i),e.push({type:"context-compacted",reason:t,strategy:l,removed:0,kept:this.messages.length}),!1;const d=this.countContiguousPresentedMessageEvidence(h);let m;try{const t=Date.now();try{m=await L({messages:this.messages,strategy:l,keepRecentTurns:n.keepRecentTurns,keepRecentTokens:n.keepRecentTokens,model:this.model,semanticEvidencePrompt:h.modelContext,maxRemovedTranscriptMessages:d,...void 0!==n.timeoutMs?{timeoutMs:n.timeoutMs}:{},...void 0!==n.maxOutputTokens?{maxOutputTokens:n.maxOutputTokens}:{},...this.structuredOutputReasoning?{structuredOutputReasoning:this.structuredOutputReasoning}:{},...this.structuredOutputProviderOptions?{structuredOutputProviderOptions:this.structuredOutputProviderOptions}:{},...u?{abortSignal:u}:{}})}finally{this.debug(e,"compaction","draft generation finished",t,{strategy:l})}if("summarize"===l&&m.removed>0&&void 0===m.semanticDraft)throw new F(D.missingObject,"structured compaction draft is missing")}catch(t){if(this.isTurnAborted(s))return o&&void 0!==s&&this.emitCancellation(e,s),!1;if("summarize"!==l||!F.isInstance(t))throw t;this.debug(e,"compaction","summarize failed, fallback to truncate",i,{message:je(t)}),c="structured checkpoint generation failed",l="truncate";const a=Date.now();m=await L({messages:this.messages,strategy:l,keepRecentTurns:n.keepRecentTurns,keepRecentTokens:n.keepRecentTokens,model:this.model,maxRemovedTranscriptMessages:d,...u?{abortSignal:u}:{}}),this.debug(e,"compaction","truncate fallback finished",a)}if(this.isTurnAborted(s))return o&&void 0!==s&&this.emitCancellation(e,s),!1;const g=m.removed>0||m.truncatedTools>0,v=Date.now(),f=g?this.buildCompactionDraft(h,m.semanticDraft,"summarize"===l&&m.removed>0,c):void 0;if(this.debug(e,"compaction","checkpoint validation finished",v,{semanticRejections:f?.semanticRejectionCount??0}),void 0!==f&&f.semanticRejectionCount>0&&this.debug(e,"compaction","rejected ungrounded semantic claims",i,{count:f.semanticRejectionCount}),void 0!==f&&!f.legacySnapshotReminderCoverageComplete)return this.debug(e,"compaction","legacy V1 snapshot exceeds reminder projection",i),e.push({type:"context-compacted",reason:t,strategy:l,removed:0,kept:this.messages.length}),!1;const y=xe(m.messages),C=[...y.messages];if(y.status===Se.repaired&&this.debug(e,"compaction","repaired malformed active Tool protocol",i,{removedToolCallIds:y.removedToolCallIds.join(","),removedToolResultIds:y.removedToolResultIds.join(",")}),1===this.compactionCheckpoint?.version&&C.some(Le))return this.debug(e,"compaction","legacy V1 snapshot remains in active history",i),e.push({type:"context-compacted",reason:t,strategy:l,removed:0,kept:this.messages.length}),!1;const b=e=>Buffer.byteLength(JSON.stringify(e),"utf8"),k=g&&b(C)<b(this.messages),T=k?C:[...this.messages],S=k?this.advanceCompactionSemanticEvidenceWatermarks(h,f?.coveredEvidenceIds??[]):h.previousSemanticEvidenceWatermarks;let x;if(k){const t=Date.now();if(void 0!==this.commitPersistedCompaction){if(void 0===f)throw new Error("compaction checkpoint snapshot is missing");x=this.commitPersistedCompaction({messages:T,expectedActiveMessages:f.expectedActiveMessages,expectedLatestCheckpointId:f.expectedLatestCheckpointId,draft:f.draft,semanticState:f.semanticState,semanticEvidenceWatermarks:S,evidenceWatermarks:f.evidenceWatermarks,...f.legacySnapshotTranscriptFragments.length>0?{legacySnapshotTranscriptFragments:f.legacySnapshotTranscriptFragments}:{}})}else{if(void 0===f)throw new Error("compaction checkpoint snapshot is missing");const e=this.compactionCheckpoint;x=ce({draft:f.draft,semanticState:f.semanticState,semanticEvidence:S,generation:(e?.generation??0)+1,transcript:{messages:{fromSequenceExclusive:e?.transcript.messages.throughSequence??-1,throughSequence:f.evidenceWatermarks.transcriptMessagesThroughSequence},toolExecutions:{fromSequenceExclusive:e?.transcript.toolExecutions.throughSequence??-1,throughSequence:f.evidenceWatermarks.toolExecutionsThroughSequence},completeness:h.completeness},...void 0!==e?{previousCheckpointId:e.id}:{}}),this.onReplace?.(T)}this.debug(e,"compaction","checkpoint commit finished",t,{durable:void 0!==this.commitPersistedCompaction}),this.messages.splice(0,this.messages.length,...T),void 0!==x&&(this.compactionCheckpoint=x),this.lastInputTokens=void 0,this.needsCompaction=!1}return e.push({type:"context-compacted",reason:t,strategy:l,removed:k?m.removed:0,kept:T.length,...k&&m.truncatedTools>0?{truncatedTools:m.truncatedTools}:{},...void 0!==r?{beforeInputTokens:r}:{},...void 0!==x?{checkpointId:x.id,checkpointGeneration:x.generation,checkpointSummaryStatus:x.summary.status}:{}}),this.debug(e,"compaction","finish",i,{reason:t,strategy:l,removed:k?m.removed:0,kept:T.length,truncatedTools:k?m.truncatedTools:0,progressed:k}),k}async recoverFromContextError(e,t){if(!this.compaction?.enabled)return!1;try{return await this.runCompaction(e,"auto",t)}catch(t){return e.push({type:"error",stage:"plan",message:je(t)}),!1}}isTurnAborted(e){return!0===e?.aborted||!0===e?.abortController.signal.aborted}execSessionIds(e){return[...e.execSessionIds].sort((e,t)=>e-t)}activeExecSessionIds(e){return this.execSessionIds(e).filter(e=>{const t=this.sessionManager?.get(e);return void 0!==t&&!g(t.state)})}interruptExecSessions(e){const t=this.execSessionIds(e);0!==t.length&&this.sessionManager?.interrupt(t).catch(e=>{process.stderr.write(`roll chat: 中断后台会话失败: ${je(e)}\n`)})}abandonTurn(e){this.closed||this.activeTurn!==e||e.abortController.signal.aborted||(this.captureCancellationActivity(e),e.aborted=!0,e.cancellationReason??=y.runtime,this.gate.abortAll("本轮事件流已停止"),this.interruptExecSessions(e),e.abortController.abort(f))}currentCancellationActivity(e){const t=this.activeExecSessionIds(e),s=[...e.pendingToolCalls.values()].filter(e=>this.toolCoordinator.hasExecutionStarted(e.toolCallId));return{execSessionIds:t,hadCompletedProgress:e.toolExecutions.length>0||e.completedStepResponses.size>0,hadInFlightWork:s.length>0||t.length>0,hadPotentialSideEffects:e.hadPotentialSideEffects||s.some(e=>e.potentialSideEffect)}}captureCancellationActivity(e){e.cancellationActivity??=this.currentCancellationActivity(e)}cancellationActivity(e){return e.cancellationActivity??this.currentCancellationActivity(e)}cancellationDisplayMessage(e){const t=e.cancellationReason??y.runtime,s=this.cancellationActivity(e);if(e.cancellationPersistenceAttempted&&!e.cancellationPersisted){const e=s.hadPotentialSideEffects?"部分操作可能已经执行,请先检查结果后再继续。":s.hadInFlightWork?"刚才的任务结果尚未确认,请先检查后再继续。":"请重新输入需要继续的内容。";return t===y.timeout?`本轮等待时间过长,已自动停止,但刚才的对话和进度未能保存。${e}`:t===y.runtime?`本轮因运行异常而中断,刚才的对话和进度未能保存。${e}`:`已停止本轮,但刚才的对话和进度未能保存。${e}`}const o=s.execSessionIds,i=se(this.capabilityManifest,Z.sessionList);switch(t){case y.user:return s.hadInFlightWork?s.hadPotentialSideEffects?"已停止本轮操作。正在进行的任务也已请求停止。之前的对话和已完成进度会保留;部分已经完成的操作不会自动撤销,请检查结果。":"已停止本轮操作。正在进行的任务也已请求停止。之前的对话和已完成进度会保留。":s.hadCompletedProgress?s.hadPotentialSideEffects?"已停止本轮回复。之前的对话和已完成进度会保留;部分已经完成的操作不会自动撤销,请检查结果。":"已停止本轮回复。之前的对话和已完成进度会保留,你可以继续输入。":"已停止本轮回复。之前的对话会保留,你可以继续输入。";case y.timeout:return 0===o.length?"本轮等待时间过长,已自动停止。之前的对话和已完成进度会保留,你可以继续输入或重试。":this.capabilityManifest.lifecycle.hostMode===X.oneShot?"本轮等待时间过长,已自动停止。正在进行的任务会随本次命令结束而停止,之后无法继续查看;请先确认实际结果,再决定是否重试。":i?`本轮等待时间过长,但任务仍在运行(任务 #${o.join(", #")})。你可以在下一条消息中继续查看进度;确认结果前请勿重复执行。`:"本轮等待时间过长。任务可能仍在运行,但 Roll 暂时无法继续查看进度;请先确认实际结果,再决定是否重试。";case y.runtime:return s.hadInFlightWork?"本轮因运行异常而中断。正在进行的任务已请求停止,最终结果尚未确认;请检查结果后再继续。":s.hadPotentialSideEffects?"本轮因运行异常而中断。之前的对话和已完成进度会保留;部分已经完成的操作不会自动撤销,请检查结果后再继续。":"本轮因运行异常而中断。之前的对话会保留,你可以重试。"}}cancellationContextMessage(e){const t=e.cancellationReason??y.runtime,s=this.cancellationActivity(e).execSessionIds,o=s.length>0?`本轮后台任务编号: ${s.join(", ")}。`:"",i=se(this.capabilityManifest,Z.sessionList);switch(t){case y.user:return`用户主动停止了本轮。${o}这些任务已收到停止请求;已完成的步骤和工具记录仍然有效,不要自动重复 outcome=success 的操作,其他状态先检查实际结果。`;case y.timeout:{const e=void 0!==this.turnTimeoutMs?`(${String(this.turnTimeoutMs)}ms)`:"";return 0===s.length?`本轮因超时${e}停止。已完成的步骤和工具记录仍然有效;继续前先检查未完成操作的实际结果。`:this.capabilityManifest.lifecycle.hostMode===X.oneShot?`本轮因超时${e}停止。${o}当前 one-shot 进程结束时会清理这些任务,后续 CLI 进程无法恢复。`:i?`本轮因超时${e}停止,但后台任务仍在当前进程运行。${o}下一轮先用 ${i} 找回并继续查看,确认结果前不要重复执行。`:`本轮因超时${e}停止,但后台任务可能仍在运行。${o}当前没有可用的任务列表工具,不能安全恢复查看。`}case y.runtime:return`本轮因运行异常中断。${o}已完成的步骤和工具记录仍然有效;未标记 outcome=success 的操作必须先检查实际结果。`}}emitCancellation(e,t){if(t.cancellationEventEmitted)return;t.cancellationReason??=y.runtime,t.cancellationEventEmitted=!0;const s=this.cancellationActivity(t).execSessionIds;e.push({type:"turn-cancelled",reason:t.cancellationReason,message:this.cancellationDisplayMessage(t),...s.length>0?{execSessionIds:s}:{}})}persistCancelledTurn(e,t,s,o,i){this.captureCancellationActivity(t);try{this.persistPendingToolCancellations(t)}catch(o){return this.messages.splice(s),t.cancellationPersistenceAttempted=!0,e.push({type:"error",stage:"execute",message:`取消工具账本持久化失败: ${je(o)}`}),void this.emitCancellation(e,t)}if(this.closed)return this.messages.splice(s),t.cancellationPersistenceAttempted=!0,void this.emitCancellation(e,t);if(!t.cancellationPersistenceAttempted){t.cancellationPersistenceAttempted=!0,this.messages.splice(s+1);const n=Oe(t),a=xe(et(n.length>0?n:o)).messages;t.cancellationPersisted=!0,this.messages.push(...a),this.messages.push(Y({context:this.cancellationContextMessage(t),completedMessages:a,toolExecutions:t.toolExecutions})),this.messages.push(k(this.cancellationDisplayMessage(t),t.cancellationReason??y.runtime)),this.debug(e,"persist","persisting cancelled turn",i,{appendedMessages:this.messages.length-s});try{this.persistMessages(this.messages.slice(s)),t.cancellationPersisted=!0}catch(o){t.cancellationPersisted=!1,this.messages.splice(s),e.push({type:"error",stage:"execute",message:`取消状态持久化失败: ${je(o)}`})}}this.emitCancellation(e,t)}persistPendingToolCancellations(e){const t=e.cancellationReason??y.runtime;for(const s of e.pendingToolCalls.values()){const o=`工具调用因本轮 ${t} 中断而取消;最终结果尚未确认`,i=E(I.cancelled,o,{raw:{cancellationReason:t,abortReason:je(e.abortController.signal.reason)},reason:t});this.persistToolExecution(P({toolCallId:s.toolCallId,agentName:s.agentName,toolName:s.toolName,input:s.input,result:i}),e),e.pendingToolCalls.delete(s.toolCallId)}}cancel(){const e=this.activeTurn;return!!e&&(this.captureCancellationActivity(e),e.aborted=!0,e.cancellationReason=y.user,this.gate.abortAll("用户取消本轮"),this.interruptExecSessions(e),e.abortController.abort(b),!0)}close(){if(void 0!==this.closePromise)return this.closePromise;this.closed=!0;const e=this.activeTurn;return e&&(this.captureCancellationActivity(e),e.aborted=!0,e.cancellationReason??=y.runtime,this.gate.abortAll(),e.abortController.abort(f)),this.closePromise=(async()=>{const e=(this.sessionManager?.close()??Promise.resolve([])).then(e=>{for(const t of e)void 0!==t.cleanupError&&process.stderr.write(`roll chat: 后台会话 ${String(t.sessionId)} 清理失败: ${t.cleanupError}\n`)},e=>{process.stderr.write(`roll chat: 会话 ${this.id} 后台清理失败: ${je(e)}\n`)});await s(e,Ee)||process.stderr.write(`roll chat: 会话 ${this.id} 在 ${String(Ee)}ms 内未完成全部关闭步骤\n`);try{this.onClose?.()}catch(e){process.stderr.write(`roll chat: 会话 ${this.id} 关闭回调失败: ${je(e)}\n`)}})(),this.closePromise}abort(){this.close().catch(e=>{process.stderr.write(`roll chat: 会话 ${this.id} 关闭失败: ${je(e)}\n`)})}debug(e,t,s,o,i){this.debugEvents&&e.push({type:"debug",stage:t,message:s,...void 0!==o?{elapsedMs:Date.now()-o}:{},...void 0!==i?{data:i}:{}})}scheduleDebug(e,t,s,o,i){if(this.debugEvents)return setTimeout(()=>{this.debug(e,t,s,o,i)},5e3)}clearDebugTimer(e){void 0!==e&&clearTimeout(e)}}