@devflow-tools/devflow 0.16.5 → 0.16.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dashboard/404.html +1 -1
- package/dist/dashboard/accuracy.html +1 -1
- package/dist/dashboard/accuracy.txt +1 -1
- package/dist/dashboard/benchmark.html +1 -1
- package/dist/dashboard/benchmark.txt +1 -1
- package/dist/dashboard/context.html +1 -1
- package/dist/dashboard/context.txt +1 -1
- package/dist/dashboard/doctor.html +1 -1
- package/dist/dashboard/doctor.txt +1 -1
- package/dist/dashboard/executions.html +1 -1
- package/dist/dashboard/executions.txt +1 -1
- package/dist/dashboard/graph.html +1 -1
- package/dist/dashboard/graph.txt +1 -1
- package/dist/dashboard/index.html +1 -1
- package/dist/dashboard/index.txt +1 -1
- package/dist/dashboard/knowledge.html +1 -1
- package/dist/dashboard/knowledge.txt +1 -1
- package/dist/dashboard/memory.html +1 -1
- package/dist/dashboard/memory.txt +1 -1
- package/dist/dashboard/replay.html +1 -1
- package/dist/dashboard/replay.txt +1 -1
- package/dist/dashboard/runs/_.html +1 -1
- package/dist/dashboard/runs/_.txt +1 -1
- package/dist/dashboard/runs.html +1 -1
- package/dist/dashboard/runs.txt +1 -1
- package/dist/dashboard/settings.html +1 -1
- package/dist/dashboard/settings.txt +1 -1
- package/dist/dashboard/timeline.html +1 -1
- package/dist/dashboard/timeline.txt +1 -1
- package/dist/dashboard/tool-metrics.html +1 -1
- package/dist/dashboard/tool-metrics.txt +1 -1
- package/dist/dashboard/workflows.html +1 -1
- package/dist/dashboard/workflows.txt +1 -1
- package/dist/hooks/hook-daemon.js +11 -11
- package/dist/hooks/post-tool-use-failure.js +1 -1
- package/dist/hooks/post-tool-use.js +3 -3
- package/dist/hooks/pre-compact.js +2 -2
- package/dist/hooks/pre-tool-use.js +1 -1
- package/dist/hooks/session-end.js +2 -2
- package/dist/hooks/user-prompt-submit.js +1 -1
- package/dist/plugin/.claude-plugin/plugin.json +1 -1
- package/dist/plugin/dist/hooks/hook-daemon.js +11 -11
- package/dist/plugin/dist/hooks/post-tool-use-failure.js +1 -1
- package/dist/plugin/dist/hooks/post-tool-use.js +3 -3
- package/dist/plugin/dist/hooks/pre-compact.js +2 -2
- package/dist/plugin/dist/hooks/pre-tool-use.js +1 -1
- package/dist/plugin/dist/hooks/session-end.js +2 -2
- package/dist/plugin/dist/hooks/user-prompt-submit.js +1 -1
- package/dist/plugin/dist/skills/devflow:react/SKILL.md +6 -16
- package/dist/plugin/package.json +7 -7
- package/dist/plugin/skills/react/SKILL.md +6 -16
- package/package.json +13 -13
- package/plugin-registry.json +29 -29
- /package/dist/dashboard/_next/static/{GZG2xsR50-1j8vTi6g2g0 → xNSXWGxbQQNrvH84gKSP1}/_buildManifest.js +0 -0
- /package/dist/dashboard/_next/static/{GZG2xsR50-1j8vTi6g2g0 → xNSXWGxbQQNrvH84gKSP1}/_ssgManifest.js +0 -0
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
3:I[55092,[],""]
|
|
3
3
|
4:I[82023,[],""]
|
|
4
4
|
5:I[68762,["736","static/chunks/736-db1a009357c222d2.js","806","static/chunks/806-195d4d5cc6c28f6c.js","697","static/chunks/697-05cf55b6c667773f.js","458","static/chunks/458-75822399a849befa.js","249","static/chunks/249-e04c1de6240fd78a.js","429","static/chunks/429-eac85fdc49353ed8.js","185","static/chunks/app/layout-4b78776412bd4fee.js"],"default",1]
|
|
5
|
-
0:["
|
|
5
|
+
0:["xNSXWGxbQQNrvH84gKSP1",[[["",{"children":["workflows",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],["",{"children":["workflows",{"children":["__PAGE__",{},[["$L1",["$","$L2",null,{}],null],null],null]},[null,["$","$L3",null,{"parallelRouterKey":"children","segmentPath":["children","workflows","children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined"}]],null]},[[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/7d4d1db5aa137b5a.css","precedence":"next","crossOrigin":"$undefined"}]],["$","$L5",null,{"children":["$","$L3",null,{"parallelRouterKey":"children","segmentPath":["children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[]}],"params":{}}]],null],null],["$L6",null]]]]
|
|
6
6
|
6:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","1",{"charSet":"utf-8"}]]
|
|
7
7
|
1:null
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import {createServer,createConnection}from'net';import {join,dirname}from'path';import {mkdirSync,lstatSync,openSync,writeFileSync,fstatSync,closeSync,readFileSync,unlinkSync,existsSync,rmSync,renameSync,readdirSync,appendFileSync as appendFileSync$1}from'fs';import {getLocalApiKey,getDaemonSocketPath,getDaemonPidPath,loadConfig,getProjectStateDir,getDaemonRegistryPath,getProjectHash}from'@devflow-tools/sdk';import {request}from'http';import {homedir}from'os';import {openGlobalDevFlowDatabase}from'@devflow-tools/database';import {fileURLToPath}from'url';import {spawn}from'child_process';import {mkdirSync as mkdirSync$1,appendFileSync,readdirSync as readdirSync$1,renameSync as renameSync$1,unlinkSync as unlinkSync$1,writeFileSync as writeFileSync$1,readFileSync as readFileSync$1,openSync as openSync$1,closeSync as closeSync$1,rmSync as rmSync$1,statSync,existsSync as existsSync$1}from'node:fs';import {dirname as dirname$1,basename,join as join$1}from'node:path';import {MemoryGate}from'@devflow-tools/memory-engine';import {randomUUID,createHash}from'crypto';import {createHash as createHash$1}from'node:crypto';import {homedir as homedir$1}from'node:os';var ce=1e4,et=3e4,D=500;function tt(r,e,t){return new Promise(n=>{try{let o=new URL(r),s=request({hostname:o.hostname,port:o.port||80,path:o.pathname+o.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":t,"Content-Length":Buffer.byteLength(e)},timeout:5e3},i=>{let a=[];i.on("data",c=>a.push(c)),i.on("end",()=>{let c=Buffer.concat(a).toString();n(i.statusCode!=null&&i.statusCode>=200&&i.statusCode<300?c:null);});});s.on("error",()=>n(null)),s.on("timeout",()=>{s.destroy(),n(null);}),s.write(e),s.end();}catch{n(null);}})}var R=class{constructor(e){this.retryScheduled=false;this.apiUrl=e?.apiUrl??process.env.DEVFLOW_API_URL??"http://127.0.0.1:13337",this.apiKey=e?.apiKey??getLocalApiKey(),this.cacheDir=e?.cacheDir??join(process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow","global"),"telemetry-cache"),this.legacyCacheDir=e?.legacyCacheDir??(e?.cacheDir?null:join(homedir(),".devflow","telemetry-cache")),this.database=e?.database??null,this.ownsDatabase=!e?.database;}async sendEvent(e){let t={...e,input:this.truncateInput(e.input)};return this.commitOrCache("tool_call",t)?(this.postHttp("/api/telemetry/tool-call",t),e.eventId):null}async sendExecutionStart(e,t,n,o,s=process.env.CLAUDE_PROJECT_DIR??process.cwd(),i=[]){let a={executionId:e,sessionId:t,skillName:n,startedAt:o,projectRoot:s,requiredMcpTools:i};this.commitOrCache("execution_start",a)&&this.postHttp("/api/telemetry/skill-execution/start",a);}async sendExecutionComplete(e,t="completed",n=Date.now(),o){let s={executionId:e,status:t,finishedAt:n,metadata:o},i=this.commitOrCache("execution_complete",s);return i&&this.postHttp("/api/telemetry/skill-execution/complete-reconciled",s),i}async sendSessionStart(e,t,n){let o={id:e,projectRoot:t,startedAt:n,label:t.split("/").pop()??"unknown"},s=this.commitOrCache("session_start",o);return s&&this.postHttp("/api/telemetry/sessions",o),s}async completeEvent(e){let t=this.commitOrCache("complete_event",e);return t&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp("/api/telemetry/tool-call/output",e)),t}async endSession(e,t=Date.now()){let n={sessionId:e,finishedAt:t},o=this.commitOrCache("session_end",n);return o&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp(`/api/telemetry/sessions/${encodeURIComponent(e)}/close`,{finishedAt:t})),o}async flushCache(){try{let n=this.getDatabase(),o=n.listTelemetryFailures({unresolvedOnly:!0,limit:D}).reverse();for(let s of o)try{this.applyOperation(s.operation,s.payload),n.resolveTelemetryFailure(s.id);}catch{}n.trimTelemetryFailures(D);}catch{}let t=[...new Set([this.cacheDir,this.legacyCacheDir].filter(n=>!!n))].filter(n=>existsSync(n)).flatMap(n=>readdirSync(n).filter(o=>o.endsWith(".json")).map(o=>{let s=join(n,o);try{return {cacheFile:s,envelope:nt(JSON.parse(readFileSync(s,"utf8")),o)}}catch{return null}})).filter(n=>n!==null).sort((n,o)=>n.envelope.timestamp-o.envelope.timestamp||le(n.envelope.operation)-le(o.envelope.operation));for(let{cacheFile:n,envelope:o}of t)try{let s=this.getDatabase();s.insertTelemetryFailure({id:o.id,operation:o.operation,payload:o.payload,error:o.failure,createdAt:o.timestamp}),this.applyOperation(o.operation,o.payload),s.resolveTelemetryFailure(o.id),unlinkSync(n);}catch{}this.database?.aggregatePendingToolMetrics();}aggregateMetrics(){return this.getDatabase().aggregatePendingToolMetrics()}async flushAndAggregate(){await this.flushCache(),this.aggregateMetrics();}close(){this.ownsDatabase&&this.database?.close(),this.database=null;}getDatabase(){return this.database??=openGlobalDevFlowDatabase(),this.database}commitOrCache(e,t){try{return this.applyOperation(e,t),!0}catch(n){return this.cacheOperation(e,t,n),false}}applyOperation(e,t){let n=this.getDatabase();switch(e){case "tool_call":this.ensureParentSession(n,t),n.insertToolCallEvent(t);return;case "execution_start":this.ensureParentSession(n,t),n.insertSkillExecution({executionId:t.executionId,sessionId:t.sessionId,skillName:t.skillName,startedAt:t.startedAt,status:"running"});return;case "execution_complete":n.reconcileSkillExecution(t.executionId,t.status,t.finishedAt,t.metadata);return;case "session_start":n.ensureSession(t),n.insertRun({id:ue(t.id),source:"hook",tool:"session",input:{projectRoot:t.projectRoot},status:"active",startedAt:t.startedAt,tokenUsed:0,metadata:{sessionId:t.id,projectRoot:t.projectRoot}});return;case "complete_event":if(!n.updateToolCallEvent(t.eventId,{output:t.output===void 0?void 0:JSON.stringify(t.output),error:t.error,duration:t.duration}))throw new Error(`Tool call event ${t.eventId} is not available for completion`);return;case "session_end":n.closeSession(t.sessionId,t.finishedAt),n.updateRun(ue(t.sessionId),{status:"completed",finishedAt:t.finishedAt});return}}ensureParentSession(e,t){let n=typeof t.sessionId=="string"?t.sessionId.trim():"";if(!n)throw new Error("Canonical session ID is required for telemetry");let o=typeof t.projectRoot=="string"&&t.projectRoot.trim()?t.projectRoot:typeof t.input?.projectRoot=="string"&&t.input.projectRoot.trim()?t.input.projectRoot:process.env.CLAUDE_PROJECT_DIR??"unknown";e.ensureSession({id:n,projectRoot:o,label:o==="unknown"?void 0:o.split("/").pop(),startedAt:Number(t.startedAt??t.timestamp??Date.now())});}cacheOperation(e,t,n){let o=Date.now(),s={id:`failure:${o}:${Math.random().toString(36).slice(2,11)}`,operation:e,payload:t,failure:n instanceof Error?n.message:String(n),timestamp:o};try{let i=this.getDatabase();i.insertTelemetryFailure({id:s.id,operation:e,payload:t,error:s.failure,createdAt:o}),i.trimTelemetryFailures(D),this.scheduleRetry();return}catch{}try{existsSync(this.cacheDir)||mkdirSync(this.cacheDir,{recursive:!0});let i=join(this.cacheDir,`${o}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync(i,JSON.stringify(s,null,2),{mode:384}),this.trimCompatibilityCache(),this.scheduleRetry();}catch{}}trimCompatibilityCache(){try{let e=readdirSync(this.cacheDir).filter(t=>t.endsWith(".json")).sort();for(let t of e.slice(0,Math.max(0,e.length-D)))unlinkSync(join(this.cacheDir,t));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},et).unref());}postHttp(e,t){tt(`${this.apiUrl}${e}`,JSON.stringify(t),this.apiKey);}truncateInput(e){let t=JSON.stringify(e);return t===void 0||t.length<=ce?e:{_truncated:true,_original_size:t.length,_preview:`${t.substring(0,ce)}...`}}};function le(r){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(r)}function ue(r){return `hook-run:${r}`}function nt(r,e){if(!r||typeof r!="object")throw new Error("Invalid telemetry cache envelope");let t=r;if(typeof t.operation=="string"&&t.payload!==void 0)return t;let n=t.type==="tool_call"?"tool_call":t.type==="execution_start"?"execution_start":null;if(!n)throw new Error("Unknown legacy telemetry cache operation");let o=t.payload??{},s=Number(o.timestamp??o.startedAt??Date.now());return {id:`legacy-cache:${e}`,operation:n,payload:o,failure:"Replayed legacy HTTP-first telemetry cache entry",timestamp:s}}function L(r){return r??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function b(r){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(L(r))}var ct=1;function lt(){if(process.env.CLAUDE_PLUGIN_ROOT)return process.env.CLAUDE_PLUGIN_ROOT;try{return join(dirname(fileURLToPath(import.meta.url)),"..","..")}catch{return process.cwd()}}function pe(r=lt()){let e=[join(r,"dist","command-registry.json"),join(r,"command-registry.json")];for(let t of e)try{if(!existsSync(t))continue;let n=JSON.parse(readFileSync(t,"utf8"));if(n.version!==ct||!ut(n.commands)){console.error(`[devflow] command registry contract mismatch: ${t}`);continue}return n.commands}catch(n){console.error(`[devflow] command registry load failed: ${n.message}`);}return null}function ut(r){return !r||typeof r!="object"||Array.isArray(r)?false:Object.values(r).every(e=>{if(!e||typeof e!="object"||Array.isArray(e))return false;let t=e;return de(t.mcpTools)&&de(t.blockedNative)})}function de(r){return Array.isArray(r)&&r.every(e=>typeof e=="string"&&e.length>0)}function me(r){let e=pe();return e?e[r]?.mcpTools??[]:[]}var q=join(homedir(),".devflow"),J=join(q,"server-refs.json"),gt="http://127.0.0.1:13337/api/health",vt=600*1e3,wt=500;function _t(){existsSync(q)||mkdirSync(q,{recursive:true});}function x(){try{if(existsSync(J))return JSON.parse(readFileSync(J,"utf-8"))}catch{}return {sessions:[],lastActivity:0}}function he(r){_t(),writeFileSync(J,JSON.stringify(r));}var P=class{constructor(e){this.process=null;this.idleTimer=null;this.onShutdown=null;this.projectRoot=e;}setOnShutdown(e){this.onShutdown=e;}addRef(e){let t=x();t.sessions.includes(e)||t.sessions.push(e),t.lastActivity=Date.now(),he(t),this.resetIdleTimer();}removeRef(e){let t=x();t.sessions=t.sessions.filter(n=>n!==e),t.lastActivity=Date.now(),he(t),t.sessions.length===0&&this.resetIdleTimer();}get activeSessions(){return x().sessions.length}async ensureRunning(){return await this.healthCheck()?true:(await this.startServer(),this.waitForReady())}get isRunning(){return this.process!==null&&!this.process.killed}async stop(){this.idleTimer&&clearTimeout(this.idleTimer),this.process&&(this.process.kill("SIGTERM"),await new Promise(e=>{let t=setTimeout(()=>{this.process&&!this.process.killed&&this.process.kill("SIGKILL"),e();},5e3);this.process?this.process.on("exit",()=>{clearTimeout(t),e();}):(clearTimeout(t),e());}),this.process=null);}async startServer(){let e=join(this.projectRoot,"apps","server","dist","main.js"),t=join(this.projectRoot,"node_modules","@devflow-tools","server","dist","main.js"),n=existsSync(e)?e:t;this.process=spawn("node",[n],{cwd:this.projectRoot,env:{...process.env,NODE_ENV:process.env.NODE_ENV||"development"},stdio:["ignore","pipe","pipe"]}),this.process.stdout?.on("data",o=>{}),this.process.stderr?.on("data",o=>{}),this.process.on("exit",o=>{this.process=null,this.onShutdown&&this.onShutdown();}),this.process.on("error",()=>{this.process=null;});}async waitForReady(){let e=Date.now()+3e4;for(;Date.now()<e;){if(await this.healthCheck())return true;await new Promise(t=>setTimeout(t,wt));}return false}healthCheck(){return new Promise(e=>{let t=new URL(gt),n=request({hostname:t.hostname,port:t.port,path:t.pathname,method:"GET",timeout:2e3},o=>{e(o.statusCode===200);});n.on("error",()=>e(false)),n.on("timeout",()=>{n.destroy(),e(false);}),n.end();})}resetIdleTimer(){this.idleTimer&&clearTimeout(this.idleTimer),!(x().sessions.length>0)&&(this.idleTimer=setTimeout(()=>{this.stop();},vt).unref());}};function fe(r,e,t){try{let n=openGlobalDevFlowDatabase();try{n.insertEvent({kind:r,timestamp:Date.now(),duration:t,success:e.success!==!1,metadata:e});}finally{n.close();}}catch{}}var B=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",Dt=getLocalApiKey(),xt=8,Ct=6e4,ye=new Map;function Pt(r){let e=new URL(r,B).pathname,t=ye.get(e);return t||(t={failures:0,lastFailure:0,openUntil:0,status:"closed"},ye.set(e,t)),t}function z(r,e,t){if(e.status===t)return;let n=e.status;e.status=t,fe("http_circuit_transition",{path:new URL(r,B).pathname,previous:n,status:t,failures:e.failures,openUntil:e.openUntil});}function W(r,e){e.failures++,e.lastFailure=Date.now(),e.failures>=xt&&(e.openUntil=Date.now()+Ct,z(r,e,"open"));}function Mt(r,e){let t=e.status!=="closed";e.failures=0,e.openUntil=0,t&&z(r,e,"closed");}var H=join(homedir(),".devflow","errors");function ge(r,e){try{existsSync(H)||mkdirSync(H,{recursive:!0});let t=`${new Date().toISOString()} | ${r} | ${e?.message??String(e)}
|
|
2
|
-
`;appendFileSync$1(join(H,"http-errors.log"),t);}catch{}}function we(r,e,t={}){let n=t.timeout??5e3,o=t.maxRetries??2,s=t.circuitBreaker??true,i=Pt(r);if(s&&i.openUntil>Date.now())return Promise.resolve();s&&i.status==="open"&&z(r,i,"half_open");let a=c=>new Promise(d=>{let l=JSON.stringify(e),m=new URL(r,B),p=request({hostname:m.hostname,port:m.port,path:m.pathname+m.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":Dt,"Content-Length":Buffer.byteLength(l)},timeout:n},f=>{if(f.resume(),f.statusCode&&f.statusCode>=400){let u=new Error(`HTTP ${f.statusCode}`);if(ge(r,u),c>0){let h=Math.min(1e3*Math.pow(2,o-c),8e3);setTimeout(()=>{a(c-1).then(d);},h);}else W(r,i),d();return}Mt(r,i),d();});p.on("error",f=>{if(ge(r,f),c>0){let u=Math.min(1e3*Math.pow(2,o-c),8e3);setTimeout(()=>{a(c-1).then(d);},u);}else W(r,i),d();}),p.on("timeout",()=>{p.destroy(),c>0?a(c-1).then(d):(W(r,i),d());}),p.write(l),p.end();});return a(o)}function M(r){if(!r||typeof r!="object"||Array.isArray(r))return {};let e=r,t={};for(let n of ["lastMcpCall","bypassCount"])if(n in e){let o=e[n];if(o==null)continue;t[n]=typeof o=="number"&&Number.isFinite(o)&&o>=0?o:0;}return t}function jt(r,e){return r.lastMcpCall===e.lastMcpCall&&r.bypassCount===e.bypassCount}function Ft(r){try{return existsSync(r)?M(JSON.parse(readFileSync(r,"utf-8"))):{}}catch{return {}}}function Nt(r,e){let t=e instanceof Error?e.message:String(e);console.error(`[devflow] Receipt ${r} skipped: ${t}`);}function _e(r){let e=b(r);for(let t of ["receipt.json","receipt-lock.sqlite","receipt-lock.sqlite-shm","receipt-lock.sqlite-wal"])try{existsSync(join(e,t))&&unlinkSync(join(e,t));}catch{}}function Ut(r,e){if(r.getHookReceipt(e)){_e(e);return}let t=join(b(e),"receipt.json");if(!existsSync(t))return;let n=Ft(t);r.updateHookReceipt(e,()=>n),_e(e);}function Re(r,e,t){let n;try{n=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250}),Ut(n,r);let o={},s=n.updateHookReceipt(r,a=>(o=M(a),M(e(o)))),i=M(s);return {receipt:i,applied:!0,changed:!jt(o,i)}}catch(o){return Nt(t,o),{receipt:{},applied:false,changed:false}}finally{try{n?.close();}catch{}}}function O(r,e){return Re(r,()=>e,"write").applied}function X(r,e){return Re(r,e,"update")}var Se={Grep:"get_project_context",Glob:"get_project_context",Agent:"get_project_context",Bash:"get_project_context",WebSearch:"get_knowledge",WebFetch:"get_knowledge"};function Lt(r){switch(r){case "WebSearch":case "WebFetch":return 1;case "Agent":return 3;case "Grep":case "Glob":return 3;case "Bash":return 5;default:return 2}}var I=class{constructor(e,t,n){this.projectRoot=e;this.sessionId=t;this.executionId=n;}getPhase(){if(!this.sessionId||!this.executionId)return "idle";let e;try{e=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250});let t=e.getContextReceipt(this.projectRoot,this.sessionId,this.executionId);if(t&&t.expiresAt>Date.now())return "context_ready";t&&e.deleteContextReceipt(this.projectRoot,this.sessionId,this.executionId);}catch{}finally{try{e?.close();}catch{}}return "context_gathering"}evaluate(e,t){let n=this.getPhase();if(t||e==="Skill")return {permissionDecision:"allow"};if(n==="context_gathering"&&Se[e])return {permissionDecision:"deny",reason:`DevFlow context required. Call mcp__devflow__get_project_context, then retry ${e}.`};if(n==="context_ready")return {permissionDecision:"allow"};let o=Se[e];if(!o)return {permissionDecision:"allow"};let s=X(this.projectRoot,c=>({...c,bypassCount:(c.bypassCount??0)+1}));if(!s.applied)return {permissionDecision:"allow"};let i=s.receipt.bypassCount??0,a=Lt(e);return i>=a?{permissionDecision:"allow",additionalContext:`\u5DF2 ${i} \u6B21\u76F4\u63A5\u4F7F\u7528 ${e}\uFF0C\u5EFA\u8BAE\u7528 mcp__devflow__${o} \u83B7\u53D6\u66F4\u7CBE\u786E\u7684\u4E0A\u4E0B\u6587\u3002`}:{permissionDecision:"allow"}}recordMcpCall(){O(this.projectRoot,{lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0});}};var Ht=10,Bt=5e3,zt=3e4,Kt=new Int32Array(new SharedArrayBuffer(4)),xe=0;function Vt(r){if(typeof r!="object"||r===null)return false;let e=r;return typeof e.rootPath=="string"&&e.rootPath.length>0&&typeof e.projectHash=="string"&&e.projectHash.length>0&&typeof e.pid=="number"&&Number.isInteger(e.pid)&&e.pid>0&&typeof e.startedAt=="number"&&Number.isFinite(e.startedAt)&&e.startedAt>=0}function Ce(r){if(!existsSync$1(r))return {entries:[],needsRepair:false};try{let e=JSON.parse(readFileSync$1(r,"utf8"));if(!Array.isArray(e))return {entries:[],needsRepair:!0};let t=e.filter(Vt);return {entries:t,needsRepair:t.length!==e.length}}catch{return {entries:[],needsRepair:true}}}function Pe(r,e){mkdirSync$1(dirname$1(r),{recursive:true});let t=`${r}.${process.pid}.${Date.now()}.${xe++}.tmp`,n;try{n=openSync$1(t,"wx"),writeFileSync$1(n,JSON.stringify(e,null,2)),closeSync$1(n),n=void 0,renameSync$1(t,r);}finally{try{n!==void 0&&closeSync$1(n);}finally{rmSync$1(t,{force:true});}}}function Me(r){try{return process.kill(r,0),!0}catch(e){return e.code!=="ESRCH"}}function Oe(r){if(typeof r!="object"||r===null)return false;let e=r;return typeof e.pid=="number"&&Number.isInteger(e.pid)&&e.pid>0&&typeof e.createdAt=="number"&&Number.isFinite(e.createdAt)&&typeof e.token=="string"&&e.token.length>0}function be(r,e){try{return readFileSync$1(r,"utf8")!==e?!1:(rmSync$1(r),!0)}catch(t){return t.code==="ENOENT"}}function Xt(r){try{let e=readFileSync$1(r,"utf8"),t;try{let o=JSON.parse(e);Oe(o)&&(t=o);}catch{}return t?Me(t.pid)?!1:be(r,e):Date.now()-statSync(r).mtimeMs>zt&&be(r,e)}catch(e){return e.code==="ENOENT"}}function Yt(r){mkdirSync$1(dirname$1(r),{recursive:true});let e=`${r}.lock`,t=Date.now()+Bt;for(;;){let n=`${process.pid}:${Date.now()}:${xe++}`,o;try{return o=openSync$1(e,"wx"),writeFileSync$1(o,JSON.stringify({pid:process.pid,createdAt:Date.now(),token:n})),closeSync$1(o),o=void 0,{path:e,token:n}}catch(s){if(o!==void 0)try{closeSync$1(o);}finally{rmSync$1(e,{force:true});}if(s.code!=="EEXIST")throw s}if(!Xt(e)){if(Date.now()>=t)throw new Error(`Timed out acquiring daemon registry lock: ${e}`);Atomics.wait(Kt,0,0,Ht);}}}function Qt(r){try{let e=readFileSync$1(r.path,"utf8"),t=JSON.parse(e);Oe(t)&&t.token===r.token&&rmSync$1(r.path);}catch(e){if(e.code!=="ENOENT")throw e}}function Ae(r){let e=getDaemonRegistryPath(),t=Yt(e);try{return r(e)}finally{Qt(t);}}function je(r,e){Ae(t=>{let n=Ce(t).entries.filter(o=>o.rootPath!==r&&Me(o.pid));n.push({rootPath:r,projectHash:getProjectHash(r),pid:e,startedAt:Date.now()}),Pe(t,n);});}function Fe(r,e){Ae(t=>{let n=Ce(t).entries.filter(o=>o.rootPath!==r||e!==void 0&&o.pid!==e);Pe(t,n);});}async function N(r){let e=!r.memory,t=r.memory??new MemoryGate(r.projectRoot),n=0,o=0;try{await t.forceWarmUp(),r.trigger==="session_end"&&(n=await t.releasePendingDistillLeases(r.sessionId)),o=t.getPendingEventCount();}finally{e&&t.close();}let s={pendingEvents:o,releasedLeases:n,trigger:r.trigger,sessionId:r.sessionId};try{let i=openGlobalDevFlowDatabase();try{let a=Date.now(),c=`distill:${a}:${Math.random().toString(36).slice(2,10)}`;i.recordMemoryDistillCheckpoint({id:c,projectRoot:r.projectRoot,sessionId:r.sessionId,trigger:r.trigger,pendingEvents:o,releasedLeases:n,createdAt:a}),i.insertEvent({kind:"memory_distill_requested",timestamp:a,success:!0,metadata:{...s,projectRoot:r.projectRoot}});}finally{i.close();}}catch{}return s}function Y(r){if(r.pendingEvents===0)return "";let e=r.sessionId?`\uFF0CsessionId=${r.sessionId}`:"";return `Memory distill checkpoint: ${r.pendingEvents} \u4E2A\u4E8B\u4EF6\u5F85\u63D0\u70BC${e}\u3002\u4E0A\u4E0B\u6587\u538B\u7F29\u5B8C\u6210\u540E\uFF0C\u8C03\u7528 mcp__devflow__memory_request_distill\uFF1B\u6309\u8FD4\u56DE\u7684 nextPage \u5206\u9875\u8BFB\u53D6\u5168\u90E8\u4E8B\u4EF6\uFF0C\u63D0\u70BC observations\uFF0C\u518D\u8C03\u7528 mcp__devflow__memory_save_distilled \u5E76\u786E\u8BA4\u4FDD\u5B58\u7ED3\u679C\u3002`}function ln(r){return Buffer.from(r,"utf8").toString("base64url")}var T=class{constructor(e){this.projectRoot=e;this.sessions=new Map;this.sessionsDir=join(b(e),"hook-sessions");}registerSession(e){let t=e.trim();if(!t)throw new Error("session_id_required");let n=this.get(t);if(n)return n.lastActivityAt=Date.now(),this.persist(n),n;let o=Date.now(),s={sessionId:t,projectRoot:this.projectRoot,requiredMcpTools:[],startedAt:o,lastActivityAt:o};return this.sessions.set(t,s),this.persist(s),s}startExecution(e,t,n){let o=this.registerSession(e);return (!o.executionId||o.skillName!==t)&&(o.executionId=`exec_${Date.now()}_${randomUUID().slice(0,8)}`),o.skillName=t,o.requiredMcpTools=[...new Set(n)],o.lastActivityAt=Date.now(),this.persist(o),o}get(e){let t=e.trim();if(!t)return null;let n=this.sessions.get(t);if(n)return n;let o=this.snapshotPath(t);if(!existsSync(o))return null;try{let s=JSON.parse(readFileSync(o,"utf8"));return s.sessionId!==t||s.projectRoot!==this.projectRoot?null:(s.requiredMcpTools=Array.isArray(s.requiredMcpTools)?s.requiredMcpTools:[],this.sessions.set(t,s),s)}catch{return null}}completeExecution(e){let t=this.get(e);if(!t)return null;let n={...t,requiredMcpTools:[...t.requiredMcpTools]};return delete t.executionId,delete t.skillName,t.requiredMcpTools=[],t.lastActivityAt=Date.now(),this.persist(t),n}removeSession(e){let t=e.trim();t&&(this.sessions.delete(t),rmSync(this.snapshotPath(t),{force:true}));}list(){return [...this.sessions.values()]}snapshotPath(e){return join(this.sessionsDir,`${ln(e)}.json`)}persist(e){mkdirSync(this.sessionsDir,{recursive:true,mode:448});let t=this.snapshotPath(e.sessionId),n=`${t}.${process.pid}.tmp`;writeFileSync(n,JSON.stringify(e),{mode:384}),renameSync(n,t);}};var mn=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",hn=getLocalApiKey();function fn(r,e){return new Promise(t=>{let n=JSON.stringify(e),o=new URL(r,mn),s=request({hostname:o.hostname,port:o.port,path:o.pathname,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":hn,"Content-Length":Buffer.byteLength(n)},timeout:5e3},()=>t());s.on("error",()=>t()),s.write(n),s.end();})}async function Q(r,e=L(),t=true,n){let o;try{o=JSON.parse(r);}catch{return}let s=o.session_id?.trim();if(!s)return;let i=new T(e),a=i.get(s),c=n?.telemetry??new R,d=!n?.telemetry;try{try{await c.flushAndAggregate();}catch{}let l;try{l=openGlobalDevFlowDatabase();let m=l.listToolCallEventsBySession(s),p=a?.executionId;if(p){let f=m.filter(y=>y.executionId===p),u=f.length,h=f.filter(y=>y.isMcpTool).length,g=u-h,v=f.filter(y=>y.toolType==="subagent").length,_=f.map(y=>y.timestamp).filter(Boolean),S=_.length>=2?Math.max(..._)-Math.min(..._):0,w=l.getExecutionObligationCompliance(p);try{l.updateSkillExecution(p,{status:"completed",finishedAt:Date.now(),totalToolCalls:u,mcpToolCalls:h,directToolCalls:g,subagentCount:v,totalDuration:S,mcpComplianceRate:w.rate}),await fn("/api/telemetry/skill-execution/complete",{executionId:p,finishedAt:Date.now(),status:"completed",summary:{totalToolCalls:u,mcpToolCalls:h,directToolCalls:g,subagentCount:v,totalDuration:S,mcpComplianceRate:w.rate}});}catch{}}t&&l.deleteHookReceipt(e),l.deleteContextReceipt(e,s);}catch{}finally{l?.close();}try{if(await N({projectRoot:e,sessionId:s,trigger:"session_end",memory:n?.memory}),n?.memory)await n.memory.closeSession(s);else {let m=new(await import('@devflow-tools/memory-engine')).MemoryGate(e);try{await m.closeSession(s);}finally{m.close();}}}catch{}try{await c.endSession(s),await c.flushAndAggregate();}catch{}}finally{d&&c.close(),i.removeSession(s);}}if(process.argv[1]?.endsWith("session-end")||process.argv[1]?.endsWith("session-end.js")){let r="";process.stdin.setEncoding("utf8"),process.stdin.on("data",e=>{r+=e;}),process.stdin.on("end",async()=>{await Q(r.trim()||process.argv[2]||""),process.exit(0);}),process.stdin.on("error",()=>process.exit(0)),setTimeout(()=>process.exit(0),1e4).unref();}var bn=/(?:^|[.!?。!?]\s*)(?:(?:please\s+)?(?:remember|memorize)\b|(?:请记|(?:请)?记住))/iu,Tn=/\b(?:(?:do\s+not|don't|dont|never|not)(?:\s+need\s+to)?|no\s+need\s+to)\s+(?:please\s+)?(?:remember|memorize)\b|(?:不要|别|不用|无需|不必|不需要)(?:再)?(?:记住|记|记忆)/iu;function $e(r){let e=r.trim();return e&&bn.test(e)&&!Tn.test(e)?e:null}function qe(r){let e=createHash$1("sha256").update(r).digest("hex").slice(0,16),t=process.env.DEVFLOW_STATE_DIR||join$1(homedir$1(),".devflow","state",e);return join$1(t,"memory-intents.jsonl")}function In(r){try{return readFileSync$1(r,"utf8").split(`
|
|
3
|
-
`).filter(Boolean).flatMap(e=>{try{let t=JSON.parse(e);return typeof t.content=="string"&&typeof t.createdAt=="number"?[t]:[]}catch{return []}})}catch{return []}}function
|
|
4
|
-
`,{mode:384});}function
|
|
1
|
+
import {createServer,createConnection}from'net';import {join,dirname}from'path';import {mkdirSync,lstatSync,openSync,writeFileSync,fstatSync,closeSync,readFileSync,unlinkSync,existsSync,rmSync,renameSync,readdirSync,appendFileSync as appendFileSync$1}from'fs';import {getLocalApiKey,getDaemonSocketPath,getDaemonPidPath,loadConfig,getProjectStateDir,getDaemonRegistryPath,getProjectHash}from'@devflow-tools/sdk';import {request}from'http';import {homedir}from'os';import {openGlobalDevFlowDatabase}from'@devflow-tools/database';import {fileURLToPath}from'url';import {spawn}from'child_process';import {mkdirSync as mkdirSync$1,appendFileSync,readdirSync as readdirSync$1,renameSync as renameSync$1,unlinkSync as unlinkSync$1,writeFileSync as writeFileSync$1,readFileSync as readFileSync$1,openSync as openSync$1,closeSync as closeSync$1,rmSync as rmSync$1,statSync,existsSync as existsSync$1}from'node:fs';import {dirname as dirname$1,basename,join as join$1}from'node:path';import {MemoryGate}from'@devflow-tools/memory-engine';import {randomUUID,createHash}from'crypto';import {createHash as createHash$1}from'node:crypto';import {homedir as homedir$1}from'node:os';var le=1e4,et=3e4,x=500;function tt(r,e,t){return new Promise(n=>{try{let o=new URL(r),s=request({hostname:o.hostname,port:o.port||80,path:o.pathname+o.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":t,"Content-Length":Buffer.byteLength(e)},timeout:5e3},i=>{let a=[];i.on("data",c=>a.push(c)),i.on("end",()=>{let c=Buffer.concat(a).toString();n(i.statusCode!=null&&i.statusCode>=200&&i.statusCode<300?c:null);});});s.on("error",()=>n(null)),s.on("timeout",()=>{s.destroy(),n(null);}),s.write(e),s.end();}catch{n(null);}})}var b=class{constructor(e){this.retryScheduled=false;this.metricAggregationScheduled=false;this.apiUrl=e?.apiUrl??process.env.DEVFLOW_API_URL??"http://127.0.0.1:13337",this.apiKey=e?.apiKey??getLocalApiKey(),this.cacheDir=e?.cacheDir??join(process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow","global"),"telemetry-cache"),this.legacyCacheDir=e?.legacyCacheDir??(e?.cacheDir?null:join(homedir(),".devflow","telemetry-cache")),this.database=e?.database??null,this.ownsDatabase=!e?.database;}async sendEvent(e){let t={...e,input:this.truncateInput(e.input)};return this.commitOrCache("tool_call",t)?(this.postHttp("/api/telemetry/tool-call",t),e.eventId):null}async sendExecutionStart(e,t,n,o,s=process.env.CLAUDE_PROJECT_DIR??process.cwd(),i=[]){let a={executionId:e,sessionId:t,skillName:n,startedAt:o,projectRoot:s,requiredMcpTools:i};this.commitOrCache("execution_start",a)&&this.postHttp("/api/telemetry/skill-execution/start",a);}async sendExecutionComplete(e,t="completed",n=Date.now(),o){let s={executionId:e,status:t,finishedAt:n,metadata:o},i=this.commitOrCache("execution_complete",s);return i&&this.postHttp("/api/telemetry/skill-execution/complete-reconciled",s),i}async sendSessionStart(e,t,n){let o={id:e,projectRoot:t,startedAt:n,label:t.split("/").pop()??"unknown"},s=this.commitOrCache("session_start",o);return s&&this.postHttp("/api/telemetry/sessions",o),s}async completeEvent(e){let t=this.commitOrCache("complete_event",e);return t&&(this.postHttp("/api/telemetry/tool-call/output",e),this.scheduleMetricAggregation()),t}async endSession(e,t=Date.now()){let n={sessionId:e,finishedAt:t},o=this.commitOrCache("session_end",n);return o&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp(`/api/telemetry/sessions/${encodeURIComponent(e)}/close`,{finishedAt:t})),o}async flushCache(){try{let n=this.getDatabase(),o=n.listTelemetryFailures({unresolvedOnly:!0,limit:x}).reverse();for(let s of o)try{this.applyOperation(s.operation,s.payload),n.resolveTelemetryFailure(s.id);}catch{}n.trimTelemetryFailures(x);}catch{}let t=[...new Set([this.cacheDir,this.legacyCacheDir].filter(n=>!!n))].filter(n=>existsSync(n)).flatMap(n=>readdirSync(n).filter(o=>o.endsWith(".json")).map(o=>{let s=join(n,o);try{return {cacheFile:s,envelope:nt(JSON.parse(readFileSync(s,"utf8")),o)}}catch{return null}})).filter(n=>n!==null).sort((n,o)=>n.envelope.timestamp-o.envelope.timestamp||ue(n.envelope.operation)-ue(o.envelope.operation));for(let{cacheFile:n,envelope:o}of t)try{let s=this.getDatabase();s.insertTelemetryFailure({id:o.id,operation:o.operation,payload:o.payload,error:o.failure,createdAt:o.timestamp}),this.applyOperation(o.operation,o.payload),s.resolveTelemetryFailure(o.id),unlinkSync(n);}catch{}this.database?.aggregatePendingToolMetrics();}aggregateMetrics(){return this.getDatabase().aggregatePendingToolMetrics()}async flushAndAggregate(){await this.flushCache(),this.aggregateMetrics();}close(){this.ownsDatabase&&this.database?.close(),this.database=null;}getDatabase(){return this.database??=openGlobalDevFlowDatabase(),this.database}commitOrCache(e,t){try{return this.applyOperation(e,t),!0}catch(n){return this.cacheOperation(e,t,n),false}}applyOperation(e,t){let n=this.getDatabase();switch(e){case "tool_call":this.ensureParentSession(n,t),n.insertToolCallEvent(t);return;case "execution_start":this.ensureParentSession(n,t),n.insertSkillExecution({executionId:t.executionId,sessionId:t.sessionId,skillName:t.skillName,startedAt:t.startedAt,status:"running",requiredMcpTools:t.requiredMcpTools});return;case "execution_complete":n.reconcileSkillExecution(t.executionId,t.status,t.finishedAt,t.metadata);return;case "session_start":n.ensureSession(t),n.insertRun({id:de(t.id),source:"hook",tool:"session",input:{projectRoot:t.projectRoot},status:"active",startedAt:t.startedAt,tokenUsed:0,metadata:{sessionId:t.id,projectRoot:t.projectRoot}});return;case "complete_event":if(!n.updateToolCallEvent(t.eventId,{output:t.output===void 0?void 0:JSON.stringify(t.output),error:t.error,duration:t.duration}))throw new Error(`Tool call event ${t.eventId} is not available for completion`);return;case "session_end":n.closeSession(t.sessionId,t.finishedAt),n.updateRun(de(t.sessionId),{status:"completed",finishedAt:t.finishedAt});return}}ensureParentSession(e,t){let n=typeof t.sessionId=="string"?t.sessionId.trim():"";if(!n)throw new Error("Canonical session ID is required for telemetry");let o=typeof t.projectRoot=="string"&&t.projectRoot.trim()?t.projectRoot:typeof t.input?.projectRoot=="string"&&t.input.projectRoot.trim()?t.input.projectRoot:process.env.CLAUDE_PROJECT_DIR??"unknown";e.ensureSession({id:n,projectRoot:o,label:o==="unknown"?void 0:o.split("/").pop(),startedAt:Number(t.startedAt??t.timestamp??Date.now())});}cacheOperation(e,t,n){let o=Date.now(),s={id:`failure:${o}:${Math.random().toString(36).slice(2,11)}`,operation:e,payload:t,failure:n instanceof Error?n.message:String(n),timestamp:o};try{let i=this.getDatabase();i.insertTelemetryFailure({id:s.id,operation:e,payload:t,error:s.failure,createdAt:o}),i.trimTelemetryFailures(x),this.scheduleRetry();return}catch{}try{existsSync(this.cacheDir)||mkdirSync(this.cacheDir,{recursive:!0});let i=join(this.cacheDir,`${o}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync(i,JSON.stringify(s,null,2),{mode:384}),this.trimCompatibilityCache(),this.scheduleRetry();}catch{}}trimCompatibilityCache(){try{let e=readdirSync(this.cacheDir).filter(t=>t.endsWith(".json")).sort();for(let t of e.slice(0,Math.max(0,e.length-x)))unlinkSync(join(this.cacheDir,t));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},et).unref());}scheduleMetricAggregation(){if(this.metricAggregationScheduled||process.env.DEVFLOW_HOOK_DEGRADED==="1")return;this.metricAggregationScheduled=true,setTimeout(()=>{this.metricAggregationScheduled=false;try{this.getDatabase().aggregatePendingToolMetrics(50)===50&&this.scheduleMetricAggregation();}catch{}},25).unref();}postHttp(e,t){tt(`${this.apiUrl}${e}`,JSON.stringify(t),this.apiKey);}truncateInput(e){let t=JSON.stringify(e);return t===void 0||t.length<=le?e:{_truncated:true,_original_size:t.length,_preview:`${t.substring(0,le)}...`}}};function ue(r){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(r)}function de(r){return `hook-run:${r}`}function nt(r,e){if(!r||typeof r!="object")throw new Error("Invalid telemetry cache envelope");let t=r;if(typeof t.operation=="string"&&t.payload!==void 0)return t;let n=t.type==="tool_call"?"tool_call":t.type==="execution_start"?"execution_start":null;if(!n)throw new Error("Unknown legacy telemetry cache operation");let o=t.payload??{},s=Number(o.timestamp??o.startedAt??Date.now());return {id:`legacy-cache:${e}`,operation:n,payload:o,failure:"Replayed legacy HTTP-first telemetry cache entry",timestamp:s}}function $(r){return r??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function T(r){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir($(r))}var ct=1;function lt(){if(process.env.CLAUDE_PLUGIN_ROOT)return process.env.CLAUDE_PLUGIN_ROOT;try{return join(dirname(fileURLToPath(import.meta.url)),"..","..")}catch{return process.cwd()}}function me(r=lt()){let e=[join(r,"dist","command-registry.json"),join(r,"command-registry.json")];for(let t of e)try{if(!existsSync(t))continue;let n=JSON.parse(readFileSync(t,"utf8"));if(n.version!==ct||!ut(n.commands)){console.error(`[devflow] command registry contract mismatch: ${t}`);continue}return n.commands}catch(n){console.error(`[devflow] command registry load failed: ${n.message}`);}return null}function ut(r){return !r||typeof r!="object"||Array.isArray(r)?false:Object.values(r).every(e=>{if(!e||typeof e!="object"||Array.isArray(e))return false;let t=e;return pe(t.mcpTools)&&pe(t.blockedNative)})}function pe(r){return Array.isArray(r)&&r.every(e=>typeof e=="string"&&e.length>0)}function he(r){let e=me();return e?e[r]?.mcpTools??[]:[]}var J=join(homedir(),".devflow"),G=join(J,"server-refs.json"),yt="http://127.0.0.1:13337/api/health",vt=600*1e3,wt=500;function _t(){existsSync(J)||mkdirSync(J,{recursive:true});}function C(){try{if(existsSync(G))return JSON.parse(readFileSync(G,"utf-8"))}catch{}return {sessions:[],lastActivity:0}}function fe(r){_t(),writeFileSync(G,JSON.stringify(r));}var P=class{constructor(e){this.process=null;this.idleTimer=null;this.onShutdown=null;this.projectRoot=e;}setOnShutdown(e){this.onShutdown=e;}addRef(e){let t=C();t.sessions.includes(e)||t.sessions.push(e),t.lastActivity=Date.now(),fe(t),this.resetIdleTimer();}removeRef(e){let t=C();t.sessions=t.sessions.filter(n=>n!==e),t.lastActivity=Date.now(),fe(t),t.sessions.length===0&&this.resetIdleTimer();}get activeSessions(){return C().sessions.length}async ensureRunning(){return await this.healthCheck()?true:(await this.startServer(),this.waitForReady())}get isRunning(){return this.process!==null&&!this.process.killed}async stop(){this.idleTimer&&clearTimeout(this.idleTimer),this.process&&(this.process.kill("SIGTERM"),await new Promise(e=>{let t=setTimeout(()=>{this.process&&!this.process.killed&&this.process.kill("SIGKILL"),e();},5e3);this.process?this.process.on("exit",()=>{clearTimeout(t),e();}):(clearTimeout(t),e());}),this.process=null);}async startServer(){let e=join(this.projectRoot,"apps","server","dist","main.js"),t=join(this.projectRoot,"node_modules","@devflow-tools","server","dist","main.js"),n=existsSync(e)?e:t;this.process=spawn("node",[n],{cwd:this.projectRoot,env:{...process.env,NODE_ENV:process.env.NODE_ENV||"development"},stdio:["ignore","pipe","pipe"]}),this.process.stdout?.on("data",o=>{}),this.process.stderr?.on("data",o=>{}),this.process.on("exit",o=>{this.process=null,this.onShutdown&&this.onShutdown();}),this.process.on("error",()=>{this.process=null;});}async waitForReady(){let e=Date.now()+3e4;for(;Date.now()<e;){if(await this.healthCheck())return true;await new Promise(t=>setTimeout(t,wt));}return false}healthCheck(){return new Promise(e=>{let t=new URL(yt),n=request({hostname:t.hostname,port:t.port,path:t.pathname,method:"GET",timeout:2e3},o=>{e(o.statusCode===200);});n.on("error",()=>e(false)),n.on("timeout",()=>{n.destroy(),e(false);}),n.end();})}resetIdleTimer(){this.idleTimer&&clearTimeout(this.idleTimer),!(C().sessions.length>0)&&(this.idleTimer=setTimeout(()=>{this.stop();},vt).unref());}};function ge(r,e,t){try{let n=openGlobalDevFlowDatabase();try{n.insertEvent({kind:r,timestamp:Date.now(),duration:t,success:e.success!==!1,metadata:e});}finally{n.close();}}catch{}}var z=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",Dt=getLocalApiKey(),xt=8,Ct=6e4,ye=new Map;function Mt(r){let e=new URL(r,z).pathname,t=ye.get(e);return t||(t={failures:0,lastFailure:0,openUntil:0,status:"closed"},ye.set(e,t)),t}function K(r,e,t){if(e.status===t)return;let n=e.status;e.status=t,ge("http_circuit_transition",{path:new URL(r,z).pathname,previous:n,status:t,failures:e.failures,openUntil:e.openUntil});}function H(r,e){e.failures++,e.lastFailure=Date.now(),e.failures>=xt&&(e.openUntil=Date.now()+Ct,K(r,e,"open"));}function Pt(r,e){let t=e.status!=="closed";e.failures=0,e.openUntil=0,t&&K(r,e,"closed");}var B=join(homedir(),".devflow","errors");function ve(r,e){try{existsSync(B)||mkdirSync(B,{recursive:!0});let t=`${new Date().toISOString()} | ${r} | ${e?.message??String(e)}
|
|
2
|
+
`;appendFileSync$1(join(B,"http-errors.log"),t);}catch{}}function _e(r,e,t={}){let n=t.timeout??5e3,o=t.maxRetries??2,s=t.circuitBreaker??true,i=Mt(r);if(s&&i.openUntil>Date.now())return Promise.resolve();s&&i.status==="open"&&K(r,i,"half_open");let a=c=>new Promise(u=>{let l=JSON.stringify(e),m=new URL(r,z),p=request({hostname:m.hostname,port:m.port,path:m.pathname+m.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":Dt,"Content-Length":Buffer.byteLength(l)},timeout:n},f=>{if(f.resume(),f.statusCode&&f.statusCode>=400){let d=new Error(`HTTP ${f.statusCode}`);if(ve(r,d),c>0){let h=Math.min(1e3*Math.pow(2,o-c),8e3);setTimeout(()=>{a(c-1).then(u);},h);}else H(r,i),u();return}Pt(r,i),u();});p.on("error",f=>{if(ve(r,f),c>0){let d=Math.min(1e3*Math.pow(2,o-c),8e3);setTimeout(()=>{a(c-1).then(u);},d);}else H(r,i),u();}),p.on("timeout",()=>{p.destroy(),c>0?a(c-1).then(u):(H(r,i),u());}),p.write(l),p.end();});return a(o)}function O(r){if(!r||typeof r!="object"||Array.isArray(r))return {};let e=r,t={};for(let n of ["lastMcpCall","bypassCount"])if(n in e){let o=e[n];if(o==null)continue;t[n]=typeof o=="number"&&Number.isFinite(o)&&o>=0?o:0;}return t}function jt(r,e){return r.lastMcpCall===e.lastMcpCall&&r.bypassCount===e.bypassCount}function Ft(r){try{return existsSync(r)?O(JSON.parse(readFileSync(r,"utf-8"))):{}}catch{return {}}}function Nt(r,e){let t=e instanceof Error?e.message:String(e);console.error(`[devflow] Receipt ${r} skipped: ${t}`);}function Se(r){let e=T(r);for(let t of ["receipt.json","receipt-lock.sqlite","receipt-lock.sqlite-shm","receipt-lock.sqlite-wal"])try{existsSync(join(e,t))&&unlinkSync(join(e,t));}catch{}}function Lt(r,e){if(r.getHookReceipt(e)){Se(e);return}let t=join(T(e),"receipt.json");if(!existsSync(t))return;let n=Ft(t);r.updateHookReceipt(e,()=>n),Se(e);}function be(r,e,t){let n;try{n=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250}),Lt(n,r);let o={},s=n.updateHookReceipt(r,a=>(o=O(a),O(e(o)))),i=O(s);return {receipt:i,applied:!0,changed:!jt(o,i)}}catch(o){return Nt(t,o),{receipt:{},applied:false,changed:false}}finally{try{n?.close();}catch{}}}function A(r,e){return be(r,()=>e,"write").applied}function Y(r,e){return be(r,e,"update")}var Ee={Grep:"get_project_context",Glob:"get_project_context",Agent:"get_project_context",Bash:"get_project_context",WebSearch:"get_knowledge",WebFetch:"get_knowledge"};function Ut(r){switch(r){case "WebSearch":case "WebFetch":return 1;case "Agent":return 3;case "Grep":case "Glob":return 3;case "Bash":return 5;default:return 2}}var k=class{constructor(e,t,n){this.projectRoot=e;this.sessionId=t;this.executionId=n;}getPhase(){if(!this.sessionId||!this.executionId)return "idle";let e;try{e=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250});let t=e.getContextReceipt(this.projectRoot,this.sessionId,this.executionId);if(t&&t.expiresAt>Date.now())return "context_ready";t&&e.deleteContextReceipt(this.projectRoot,this.sessionId,this.executionId);}catch{}finally{try{e?.close();}catch{}}return "context_gathering"}evaluate(e,t){let n=this.getPhase();if(t||e==="Skill")return {permissionDecision:"allow"};if(n==="context_gathering"&&Ee[e])return {permissionDecision:"deny",reason:`DevFlow context required. Call mcp__devflow__get_project_context, then retry ${e}.`};if(n==="context_ready")return {permissionDecision:"allow"};let o=Ee[e];if(!o)return {permissionDecision:"allow"};let s=Y(this.projectRoot,c=>({...c,bypassCount:(c.bypassCount??0)+1}));if(!s.applied)return {permissionDecision:"allow"};let i=s.receipt.bypassCount??0,a=Ut(e);return i>=a?{permissionDecision:"allow",additionalContext:`\u5DF2 ${i} \u6B21\u76F4\u63A5\u4F7F\u7528 ${e}\uFF0C\u5EFA\u8BAE\u7528 mcp__devflow__${o} \u83B7\u53D6\u66F4\u7CBE\u786E\u7684\u4E0A\u4E0B\u6587\u3002`}:{permissionDecision:"allow"}}recordMcpCall(){A(this.projectRoot,{lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0});}};var Ht=10,Bt=5e3,zt=3e4,Kt=new Int32Array(new SharedArrayBuffer(4)),Ce=0;function Vt(r){if(typeof r!="object"||r===null)return false;let e=r;return typeof e.rootPath=="string"&&e.rootPath.length>0&&typeof e.projectHash=="string"&&e.projectHash.length>0&&typeof e.pid=="number"&&Number.isInteger(e.pid)&&e.pid>0&&typeof e.startedAt=="number"&&Number.isFinite(e.startedAt)&&e.startedAt>=0}function Me(r){if(!existsSync$1(r))return {entries:[],needsRepair:false};try{let e=JSON.parse(readFileSync$1(r,"utf8"));if(!Array.isArray(e))return {entries:[],needsRepair:!0};let t=e.filter(Vt);return {entries:t,needsRepair:t.length!==e.length}}catch{return {entries:[],needsRepair:true}}}function Pe(r,e){mkdirSync$1(dirname$1(r),{recursive:true});let t=`${r}.${process.pid}.${Date.now()}.${Ce++}.tmp`,n;try{n=openSync$1(t,"wx"),writeFileSync$1(n,JSON.stringify(e,null,2)),closeSync$1(n),n=void 0,renameSync$1(t,r);}finally{try{n!==void 0&&closeSync$1(n);}finally{rmSync$1(t,{force:true});}}}function Oe(r){try{return process.kill(r,0),!0}catch(e){return e.code!=="ESRCH"}}function Ae(r){if(typeof r!="object"||r===null)return false;let e=r;return typeof e.pid=="number"&&Number.isInteger(e.pid)&&e.pid>0&&typeof e.createdAt=="number"&&Number.isFinite(e.createdAt)&&typeof e.token=="string"&&e.token.length>0}function Te(r,e){try{return readFileSync$1(r,"utf8")!==e?!1:(rmSync$1(r),!0)}catch(t){return t.code==="ENOENT"}}function Xt(r){try{let e=readFileSync$1(r,"utf8"),t;try{let o=JSON.parse(e);Ae(o)&&(t=o);}catch{}return t?Oe(t.pid)?!1:Te(r,e):Date.now()-statSync(r).mtimeMs>zt&&Te(r,e)}catch(e){return e.code==="ENOENT"}}function Yt(r){mkdirSync$1(dirname$1(r),{recursive:true});let e=`${r}.lock`,t=Date.now()+Bt;for(;;){let n=`${process.pid}:${Date.now()}:${Ce++}`,o;try{return o=openSync$1(e,"wx"),writeFileSync$1(o,JSON.stringify({pid:process.pid,createdAt:Date.now(),token:n})),closeSync$1(o),o=void 0,{path:e,token:n}}catch(s){if(o!==void 0)try{closeSync$1(o);}finally{rmSync$1(e,{force:true});}if(s.code!=="EEXIST")throw s}if(!Xt(e)){if(Date.now()>=t)throw new Error(`Timed out acquiring daemon registry lock: ${e}`);Atomics.wait(Kt,0,0,Ht);}}}function Qt(r){try{let e=readFileSync$1(r.path,"utf8"),t=JSON.parse(e);Ae(t)&&t.token===r.token&&rmSync$1(r.path);}catch(e){if(e.code!=="ENOENT")throw e}}function je(r){let e=getDaemonRegistryPath(),t=Yt(e);try{return r(e)}finally{Qt(t);}}function Fe(r,e){je(t=>{let n=Me(t).entries.filter(o=>o.rootPath!==r&&Oe(o.pid));n.push({rootPath:r,projectHash:getProjectHash(r),pid:e,startedAt:Date.now()}),Pe(t,n);});}function Ne(r,e){je(t=>{let n=Me(t).entries.filter(o=>o.rootPath!==r||e!==void 0&&o.pid!==e);Pe(t,n);});}async function L(r){let e=!r.memory,t=r.memory??new MemoryGate(r.projectRoot),n=0,o=0;try{await t.forceWarmUp(),r.trigger==="session_end"&&(n=await t.releasePendingDistillLeases(r.sessionId)),o=t.getPendingEventCount();}finally{e&&t.close();}let s={pendingEvents:o,releasedLeases:n,trigger:r.trigger,sessionId:r.sessionId};try{let i=openGlobalDevFlowDatabase();try{let a=Date.now(),c=`distill:${a}:${Math.random().toString(36).slice(2,10)}`;i.recordMemoryDistillCheckpoint({id:c,projectRoot:r.projectRoot,sessionId:r.sessionId,trigger:r.trigger,pendingEvents:o,releasedLeases:n,createdAt:a}),i.insertEvent({kind:"memory_distill_requested",timestamp:a,success:!0,metadata:{...s,projectRoot:r.projectRoot}});}finally{i.close();}}catch{}return s}function Q(r){if(r.pendingEvents===0)return "";let e=r.sessionId?`\uFF0CsessionId=${r.sessionId}`:"";return `Memory distill checkpoint: ${r.pendingEvents} \u4E2A\u4E8B\u4EF6\u5F85\u63D0\u70BC${e}\u3002\u4E0A\u4E0B\u6587\u538B\u7F29\u5B8C\u6210\u540E\uFF0C\u8C03\u7528 mcp__devflow__memory_request_distill\uFF1B\u6309\u8FD4\u56DE\u7684 nextPage \u5206\u9875\u8BFB\u53D6\u5168\u90E8\u4E8B\u4EF6\uFF0C\u63D0\u70BC observations\uFF0C\u518D\u8C03\u7528 mcp__devflow__memory_save_distilled \u5E76\u786E\u8BA4\u4FDD\u5B58\u7ED3\u679C\u3002`}function ln(r){return Buffer.from(r,"utf8").toString("base64url")}var I=class{constructor(e){this.projectRoot=e;this.sessions=new Map;this.sessionsDir=join(T(e),"hook-sessions");}registerSession(e){let t=e.trim();if(!t)throw new Error("session_id_required");let n=this.get(t);if(n)return n.lastActivityAt=Date.now(),this.persist(n),n;let o=Date.now(),s={sessionId:t,projectRoot:this.projectRoot,requiredMcpTools:[],startedAt:o,lastActivityAt:o};return this.sessions.set(t,s),this.persist(s),s}startExecution(e,t,n){let o=this.registerSession(e);return (!o.executionId||o.skillName!==t)&&(o.executionId=`exec_${Date.now()}_${randomUUID().slice(0,8)}`),o.skillName=t,o.requiredMcpTools=[...new Set(n)],o.lastActivityAt=Date.now(),this.persist(o),o}get(e){let t=e.trim();if(!t)return null;let n=this.sessions.get(t);if(n)return n;let o=this.snapshotPath(t);if(!existsSync(o))return null;try{let s=JSON.parse(readFileSync(o,"utf8"));return s.sessionId!==t||s.projectRoot!==this.projectRoot?null:(s.requiredMcpTools=Array.isArray(s.requiredMcpTools)?s.requiredMcpTools:[],this.sessions.set(t,s),s)}catch{return null}}completeExecution(e){let t=this.get(e);if(!t)return null;let n={...t,requiredMcpTools:[...t.requiredMcpTools]};return delete t.executionId,delete t.skillName,t.requiredMcpTools=[],t.lastActivityAt=Date.now(),this.persist(t),n}removeSession(e){let t=e.trim();t&&(this.sessions.delete(t),rmSync(this.snapshotPath(t),{force:true}));}list(){return [...this.sessions.values()]}snapshotPath(e){return join(this.sessionsDir,`${ln(e)}.json`)}persist(e){mkdirSync(this.sessionsDir,{recursive:true,mode:448});let t=this.snapshotPath(e.sessionId),n=`${t}.${process.pid}.tmp`;writeFileSync(n,JSON.stringify(e),{mode:384}),renameSync(n,t);}};var mn=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",hn=getLocalApiKey();function fn(r,e){return new Promise(t=>{let n=JSON.stringify(e),o=new URL(r,mn),s=request({hostname:o.hostname,port:o.port,path:o.pathname,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":hn,"Content-Length":Buffer.byteLength(n)},timeout:5e3},()=>t());s.on("error",()=>t()),s.write(n),s.end();})}async function Z(r,e=$(),t=true,n){let o;try{o=JSON.parse(r);}catch{return}let s=o.session_id?.trim();if(!s)return;let i=new I(e),a=i.get(s),c=n?.telemetry??new b,u=!n?.telemetry;try{try{await c.flushAndAggregate();}catch{}let l;try{l=openGlobalDevFlowDatabase();let m=l.listToolCallEventsBySession(s),p=a?.executionId;if(p){let f=m.filter(S=>S.executionId===p),d=f.length,h=f.filter(S=>S.isMcpTool).length,g=d-h,y=f.filter(S=>S.toolType==="subagent").length,_=Date.now(),E=l.getSkillExecution(p),v=E?.startedAt!=null?Math.max(0,_-E.startedAt):0,w=l.getExecutionObligationCompliance(p);try{l.updateSkillExecution(p,{status:"completed",finishedAt:_,totalToolCalls:d,mcpToolCalls:h,directToolCalls:g,subagentCount:y,totalDuration:v,mcpComplianceRate:w.rate}),await fn("/api/telemetry/skill-execution/complete",{executionId:p,finishedAt:_,status:"completed",summary:{totalToolCalls:d,mcpToolCalls:h,directToolCalls:g,subagentCount:y,totalDuration:v,mcpComplianceRate:w.rate}});}catch{}}t&&l.deleteHookReceipt(e),l.deleteContextReceipt(e,s);}catch{}finally{l?.close();}try{if(await L({projectRoot:e,sessionId:s,trigger:"session_end",memory:n?.memory}),n?.memory)await n.memory.closeSession(s);else {let m=new(await import('@devflow-tools/memory-engine')).MemoryGate(e);try{await m.closeSession(s);}finally{m.close();}}}catch{}try{await c.endSession(s),await c.flushAndAggregate();}catch{}}finally{u&&c.close(),i.removeSession(s);}}if(process.argv[1]?.endsWith("session-end")||process.argv[1]?.endsWith("session-end.js")){let r="";process.stdin.setEncoding("utf8"),process.stdin.on("data",e=>{r+=e;}),process.stdin.on("end",async()=>{await Z(r.trim()||process.argv[2]||""),process.exit(0);}),process.stdin.on("error",()=>process.exit(0)),setTimeout(()=>process.exit(0),1e4).unref();}var bn=/(?:^|[.!?。!?]\s*)(?:(?:please\s+)?(?:remember|memorize)\b|(?:请记|(?:请)?记住))/iu,Tn=/\b(?:(?:do\s+not|don't|dont|never|not)(?:\s+need\s+to)?|no\s+need\s+to)\s+(?:please\s+)?(?:remember|memorize)\b|(?:不要|别|不用|无需|不必|不需要)(?:再)?(?:记住|记|记忆)/iu;function qe(r){let e=r.trim();return e&&bn.test(e)&&!Tn.test(e)?e:null}function Je(r){let e=createHash$1("sha256").update(r).digest("hex").slice(0,16),t=process.env.DEVFLOW_STATE_DIR||join$1(homedir$1(),".devflow","state",e);return join$1(t,"memory-intents.jsonl")}function In(r){try{return readFileSync$1(r,"utf8").split(`
|
|
3
|
+
`).filter(Boolean).flatMap(e=>{try{let t=JSON.parse(e);return typeof t.content=="string"&&typeof t.createdAt=="number"?[t]:[]}catch{return []}})}catch{return []}}function Ge(r,e){let t=Je(r);mkdirSync$1(dirname$1(t),{recursive:true}),appendFileSync(t,`${JSON.stringify(e)}
|
|
4
|
+
`,{mode:384});}function We(r){let e=Je(r),t=`${e}.claim-`,n=`${basename(e)}.claim-`,o=(()=>{try{return readdirSync$1(dirname$1(e)).filter(a=>a.startsWith(n)&&!a.includes(".tmp-")).sort()[0]}catch{return}})(),s=o?join$1(dirname$1(e),o):`${t}${Date.now()}-${process.pid}`;if(!o)try{renameSync$1(e,s);}catch{return null}let i=In(s);if(i.length===0){try{unlinkSync$1(s);}catch{}return null}return {path:s,intents:i}}function He(r){if(r.intents.shift(),r.intents.length===0){try{unlinkSync$1(r.path);}catch{}return}let e=`${r.path}.tmp-${process.pid}`;writeFileSync$1(e,r.intents.map(t=>JSON.stringify(t)).join(`
|
|
5
5
|
`)+`
|
|
6
|
-
`,{mode:384}),renameSync$1(e,r.path);}function U(r){let e=r.trim().replace(/^\//,"");if(!e.startsWith("devflow:"))return null;let t=e.slice(8).replace(/^devflow-/,"");return /^[a-z0-9][a-z0-9-]*$/i.test(t)?`devflow:${t.toLowerCase()}`:null}function
|
|
7
|
-
`);if(t=m.pop()??"",Buffer.byteLength(t,"utf8")>
|
|
6
|
+
`,{mode:384}),renameSync$1(e,r.path);}function U(r){let e=r.trim().replace(/^\//,"");if(!e.startsWith("devflow:"))return null;let t=e.slice(8).replace(/^devflow-/,"");return /^[a-z0-9][a-z0-9-]*$/i.test(t)?`devflow:${t.toLowerCase()}`:null}function Be(r){let e=r.trimStart().match(/^\/(devflow:[a-z0-9][a-z0-9-]*)\b/i);if(!e)return null;let t=U(e[1]);return t?{rawName:e[1],skillName:t}:null}function D(r,e){return e?`evt_tool_${createHash("sha256").update(`${r}\0${e}`).digest("hex").slice(0,24)}`:`evt_${Date.now()}_${Math.random().toString(36).slice(2,11)}`}var Gn=600*1e3,Wn=2e3,Hn=250,Bn=2e3,zn=1e3,ze=1024*1024,Kn=2*1024*1024,Vn=6e4,Xn=100,Yn={Agent:"get_project_context",Bash:"get_project_context",Glob:"get_project_context",Grep:"get_project_context",WebSearch:"get_knowledge",WebFetch:"get_knowledge"};function Qn(r){switch(r){case "Read":case "Glob":return "file_read";case "Write":case "Edit":case "NotebookEdit":return "file_write";case "Bash":return "bash_command";case "Agent":return "subagent";case "Skill":return "skill_invoke";default:return "tool_use"}}function oe(r){return typeof r=="string"&&r.trim().length>0}function Zn(r,e,t=process.env,n){if(!r.startsWith("mcp__devflow__"))return e;let o=e!==null&&typeof e=="object"&&!Array.isArray(e)?e:{};if(oe(o.projectRoot))return o;let s=oe(t.CLAUDE_PROJECT_DIR)?t.CLAUDE_PROJECT_DIR:n;return oe(s)&&(o.projectRoot=s),o}var re=class{constructor(e,t){this.socketIdentity=null;this.pidIdentity=null;this.server=null;this.activeSessionIds=new Set;this.eventMap=[];this.idleTimer=null;this.telemetryFlushTimer=null;this.successfulTelemetryOperations=0;this.shutdownPromise=null;this.explicitMemoryGate=null;this.memoryWarmup=null;this.explicitIntentFlush=null;this.projectRoot=e,this.socketPath=getDaemonSocketPath(e),this.pidPath=getDaemonPidPath(e),this.telemetry=new b,this.runtimeStore=new I(this.projectRoot),this.serverManager=new P(e),t&&this.handleSessionStart(t);}async start(){mkdirSync(dirname(this.socketPath),{recursive:true,mode:448}),await this.cleanStaleStartupResources(),this.server=createServer(e=>this.handleConnection(e));try{await this.listen(),this.writePidFile(),Fe(this.projectRoot,process.pid);}catch(e){throw await this.closeServer(),this.cleanupOwnedResources(),e}this.server.on("error",e=>{console.error("[devflow-daemon] Server error:",e.message);}),console.error("[devflow-daemon] Listening on",this.socketPath),await this.telemetry.flushAndAggregate(),this.telemetryFlushTimer=setInterval(()=>{this.flushTelemetry();},Vn),this.telemetryFlushTimer.unref(),this.resetIdleTimer();}handleConnection(e){let t="",n=false,o=false,s=0,i=Promise.resolve();e.setEncoding("utf8");let a=()=>{o||(o=true,clearTimeout(c),e.destroy());},c=setTimeout(()=>{a();},Wn).unref(),u=async l=>{if(!o){o=true,clearTimeout(c);try{await this.writeFrame(e,l);}catch{}finally{e.destroy();}}};e.on("data",l=>{if(o)return;t+=l;let m=t.split(`
|
|
7
|
+
`);if(t=m.pop()??"",Buffer.byteLength(t,"utf8")>ze){a();return}if(m.length===0)return;let p=0;for(let f of m){let d=Buffer.byteLength(f,"utf8");if(d>ze){a();return}p+=d+1;}if(s+p>Kn){a();return}s+=p,i=i.then(async()=>{for(let f of m){if(o)return;if(!n){let d;try{d=JSON.parse(f);}catch{await u({error:"handshake_invalid"});return}if(d?.type!=="hello"||typeof d.rootPath!="string"){await u({error:"handshake_invalid"});return}if(d.rootPath!==this.projectRoot){await u({error:"handshake_root_mismatch",expected:this.projectRoot});return}n=true,clearTimeout(c),this.resetIdleTimer();continue}if(f.trim())try{let d=JSON.parse(f),h=await this.handleRequest(d);h!==null&&!o&&await this.writeFrame(e,h);}catch(d){o||await this.writeFrame(e,{error:d.message});}}}).catch(()=>{a();}).finally(()=>{s-=p;});}),e.on("close",()=>clearTimeout(c)),e.on("error",()=>{});}writeFrame(e,t){return new Promise((n,o)=>{if(e.destroyed||!e.writable){o(new Error("Socket is not writable"));return}let s=false,i=true,a=false,c=setTimeout(()=>{e.destroy(),l(new Error("Timed out writing daemon response"));},zn),u=()=>{clearTimeout(c),e.off("close",m),e.off("error",p),e.off("drain",f);},l=h=>{a||!h&&(!s||!i)||(a=true,u(),h?o(h):n());},m=()=>l(new Error("Socket closed during write")),p=h=>l(h),f=()=>{i=true,l();};e.once("close",m),e.once("error",p),e.write(`${JSON.stringify(t)}
|
|
8
8
|
`,h=>{if(h){l(h);return}s=true,l();})||(i=false,e.once("drain",f));})}async cleanStaleStartupResources(){let e=this.getPathIdentity(this.socketPath);if(e){if(await this.isSocketAcceptingConnections())throw new Error(`Daemon socket already active for ${this.projectRoot}`);this.removePathIfOwned(this.socketPath,e,"stale socket");}let t=this.getPathIdentity(this.pidPath);t&&this.removePathIfOwned(this.pidPath,t,"stale PID file");}getPathIdentity(e){try{let t=lstatSync(e);return {dev:t.dev,ino:t.ino}}catch(t){if(t.code==="ENOENT")return null;throw t}}pathMatchesIdentity(e,t){if(!t)return false;try{let n=this.getPathIdentity(e);return n?.dev===t.dev&&n.ino===t.ino}catch(n){return console.error("[devflow-daemon] Failed to verify path ownership:",n.message),false}}writePidFile(){let e=openSync(this.pidPath,"wx",384);try{writeFileSync(e,`${process.pid}
|
|
9
|
-
`);let t=fstatSync(e);this.pidIdentity={dev:t.dev,ino:t.ino};}finally{closeSync(e);}}removePathIfOwned(e,t,n,o){try{return !this.pathMatchesIdentity(e,t)||o!==void 0&&readFileSync(e,"utf8")!==o||!this.pathMatchesIdentity(e,t)?!1:(unlinkSync(e),!0)}catch(s){return s.code!=="ENOENT"&&console.error(`[devflow-daemon] Failed to remove ${n}:`,s.message),false}}isSocketAcceptingConnections(){return new Promise((e,t)=>{let n=createConnection(this.socketPath),o=setTimeout(()=>{n.destroy(),t(new Error(`Timed out probing daemon socket ${this.socketPath}`));},Hn),s=i=>{clearTimeout(o),n.destroy(),e(i);};n.once("connect",()=>s(true)),n.once("error",i=>{let a=i.code;a==="ENOENT"||a==="ECONNREFUSED"?s(false):(clearTimeout(o),t(i));});})}listen(){return new Promise((e,t)=>{let n=this.server;if(!n){t(new Error("Daemon server is not initialized"));return}let o=i=>{n.off("listening",s),t(i);},s=()=>{n.off("error",o);try{let i=this.getPathIdentity(this.socketPath);if(!i)throw new Error(`Daemon socket missing after listen: ${this.socketPath}`);this.socketIdentity=i,e();}catch(i){t(i);}};n.once("error",o),n.once("listening",s),n.listen(this.socketPath);})}resetIdleTimer(){this.idleTimer&&clearTimeout(this.idleTimer),this.idleTimer=null,!(this.activeSessionIds.size>0)&&(this.idleTimer=setTimeout(()=>{console.error("[devflow-daemon] Idle timeout, exiting"),this.shutdown();},Gn).unref());}async handleRequest(e){switch(e.type){case "pre-tool-use":return this.handlePreToolUse(e.input);case "post-tool-use":return this.handlePostToolUse(e.input);case "post-tool-use-failure":return this.handlePostToolUseFailure(e.input);case "stop":return await this.handleStop(e.input??""),{status:"ok"};case "memory-snapshot":return this.handleMemorySnapshot();case "session-start":return this.handleSessionStart(e.input??"");case "session-end":return await this.handleSessionEnd(e.input??""),{status:"ok"};case "user-prompt-submit":return this.handleUserPromptSubmit(e.input);case "pre-compact":return this.handlePreCompact(e.input);default:return {error:"unknown request type"}}}async handlePreToolUse(e){try{if(!e||e.trim()==="")return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}};let t=JSON.parse(e);t.tool_input=Zn(t.tool_name,t.tool_input,process.env,this.projectRoot);let n=this.trackSession(t.session_id);if(t.tool_name==="Skill"){let g=t.tool_input?.skill,v=g?U(g):null;v&&this.startSkillExecution(n,v);}let o=this.runtimeStore.get(n);t.tool_name.startsWith("mcp__devflow__")&&(t.tool_input._devflow_session_id=n,t.tool_input._devflow_execution_id=o?.executionId??n,t.tool_use_id&&(t.tool_input._devflow_tool_use_id=t.tool_use_id));let s=t.tool_name.startsWith("mcp__"),i=s?t.tool_name.replace(/^mcp__[^_]+__/,""):void 0,a=new I(this.projectRoot,n,o?.executionId),c=a.getPhase(),d=a.evaluate(t.tool_name,s),l;d.permissionDecision==="deny"?l={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:d.reason??"MCP context required"}}:d.additionalContext?l={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",additionalContext:d.additionalContext}}:l={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}},t.tool_name.startsWith("mcp__devflow__")&&(l.hookSpecificOutput.updatedInput=t.tool_input);let m={...t.tool_input};delete m._devflow_session_id,delete m._devflow_execution_id,delete m._devflow_tool_use_id;let p=o?.executionId??n,u={eventId:k(n,t.tool_use_id),executionId:p,sessionId:n,projectRoot:this.projectRoot,toolUseId:t.tool_use_id,timestamp:Date.now(),toolName:t.tool_name,toolType:s?"mcp":t.tool_name==="Agent"?"subagent":"direct",isMcpTool:s,mcpToolName:i,mcpEnforced:s&&c==="context_gathering",mcpFallback:!s&&c==="context_gathering",kind:Qn(t.tool_name),input:m,duration:0,blocked:l&&l.hookSpecificOutput?.permissionDecision==="deny"},h=await this.telemetry.sendEvent({...u,blockReason:u.blocked?"DEVFLOW_CONTEXT_REQUIRED":void 0,error:u.blocked?"DEVFLOW_CONTEXT_REQUIRED":void 0});return h&&!u.blocked&&(this.eventMap.push({toolName:t.tool_name,toolUseId:t.tool_use_id,eventId:h,sessionId:n,timestamp:u.timestamp}),this.noteTelemetryOperation()),h&&u.blocked&&await this.telemetry.completeEvent({eventId:h,sessionId:n,error:"DEVFLOW_CONTEXT_REQUIRED",duration:Date.now()-u.timestamp,completedAt:Date.now()}),l}catch{return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}}}}trackSession(e){let t=e?.trim()||(this.activeSessionIds.size===1?[...this.activeSessionIds][0]:"");if(!t)throw new Error("session_id_required");return this.registerSession(t),t}registerSession(e){this.activeSessionIds.has(e)||(this.activeSessionIds.size===0&&O(this.projectRoot,{bypassCount:0}),this.activeSessionIds.add(e),this.idleTimer&&(clearTimeout(this.idleTimer),this.idleTimer=null),this.serverManager.addRef(e),this.telemetry.sendSessionStart(e,this.projectRoot,Date.now()).catch(()=>{})),this.runtimeStore.registerSession(e);}startSkillExecution(e,t){let n=this.runtimeStore.get(e)?.executionId,o=this.runtimeStore.startExecution(e,t,me(t));o.executionId&&o.executionId!==n&&this.telemetry.sendExecutionStart(o.executionId,e,t,o.lastActivityAt,this.projectRoot,o.requiredMcpTools).catch(()=>{});}async handleSessionStart(e){try{let t=JSON.parse(e);if(typeof t.session_id!="string"||!t.session_id.trim())return {error:"session_id_required"};let n=t.session_id.trim();this.registerSession(n);let o=await this.getMemoryGate();await o.ensureSession(n,this.projectRoot,"Claude Code session"),await o.releasePendingDistillLeases(n);let s=o.getPendingEventCount();return {status:"ok",...s>0?{additionalContext:Y({pendingEvents:s,releasedLeases:0,trigger:"session_end"})}:{}}}catch{return {error:"session_start_invalid"}}}popEventFromMap(e,t,n){let o=this.eventMap.filter(i=>i.toolName===e&&i.sessionId===t);if(o.length===0)return null;let s=n?o.find(i=>i.toolUseId===n):o.sort((i,a)=>i.timestamp-a.timestamp)[0];return s?(this.eventMap=this.eventMap.filter(i=>i.eventId!==s.eventId),s):null}enforcePostToolUse(e){if(!Yn[e])return null;let t=Math.floor(Date.now()/1e3),n=X(this.projectRoot,i=>t-(i.lastMcpCall??0)<30?i:{...i,bypassCount:(i.bypassCount??0)+1});if(!n.applied||!n.changed)return null;let o=n.receipt.bypassCount??0;if(o===1)return null;let s;switch(e){case "Agent":s=`\u7B2C ${o} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u4F7F\u7528\u4E86 Agent \u5B50\u4EE3\u7406\u6267\u884C\u641C\u7D22\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "Bash":s=`\u7B2C ${o} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86 Bash \u547D\u4EE4\u641C\u7D22\u4EE3\u7801\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "Glob":s=`\u7B2C ${o} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86 Glob \u5339\u914D\u6587\u4EF6\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "Grep":s=`\u7B2C ${o} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86 Grep \u641C\u7D22\u4EE3\u7801\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "WebSearch":case "WebFetch":s=`\u7B2C ${o} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86\u7F51\u7EDC\u641C\u7D22\u3002\u4E0B\u6B21\u8BF7\u7528 get_knowledge MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;default:s=`\u7B2C ${o} \u6B21\u8FDD\u89C4\uFF1A\u8BF7\u4F7F\u7528 DevFlow MCP \u5DE5\u5177\u66FF\u4EE3\u76F4\u63A5\u8C03\u7528\u3002`;}return o>=3?s=`${s} \u5DF2\u8FBE\u5230\u6700\u5927\u8FDD\u89C4\u6B21\u6570\uFF0C\u540E\u7EED\u7ED5\u8FC7\u5C06\u88AB\u786C\u963B\u6B62\u3002`:o>=2?s=`${s} \u4E0B\u6B21\u8FDD\u89C4\u5C06\u88AB\u963B\u6B62\u3002`:s=`${s} \u8BF7\u7ACB\u5373\u7EA0\u6B63\u3002`,s}async handlePostToolUse(e){if(!e)return null;let t;try{t=JSON.parse(e);}catch{return null}let n=t.tool_name||"",o=t.tool_input||{},s=t.tool_response,i=this.trackSession(t.session_id);if(n==="Skill"){let w=o?.skill,y=w?U(w):null;y&&(this.startSkillExecution(i,y),O(this.projectRoot,{lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0}));}n.startsWith("mcp__")&&new I(this.projectRoot).recordMcpCall();let a=this.popEventFromMap(n,i,t.tool_use_id);if(a){let w=typeof s=="string"?s:JSON.stringify(s),y=w.length>5e3?{_truncated:true,_originalSize:w.length,text:w.slice(0,5e3)}:s,ze=Date.now()-a.timestamp;await this.telemetry.completeEvent({eventId:a.eventId,sessionId:i,output:y,duration:ze,completedAt:Date.now()})&&this.noteTelemetryOperation();}let c=typeof s=="object"&&s!==null?s:null,d=c?.exitCode,l=c?.stderr,m=/^\s*(ls|cat|pwd|cd|echo|head|tail|wc|which|whoami|date|env|printenv|id|hostname|uname)\b/,p=o.command||"",f=JSON.stringify(s),u=Buffer.byteLength(f,"utf8"),h=this.countResults(s),g,v,_=true;if(n.startsWith("mcp__"))g="mcp_call",v={mcpTool:n.replace(/^mcp__[^_]+__/,""),query:o.query??null,resultCount:h,resultSizeBytes:u};else if(n==="Read"||n==="Write"||n==="Edit")g=n==="Read"?"file_read":"file_write",v={filePath:o.file_path??null,fileContentSize:u};else if(n==="Bash"){let w=m.test(p),y=d!==void 0&&d!==0;_=!w||y,g=y?"bash_error":"bash_command",v={command:p||n,exitCode:d??null,stderr:l??null};}else n==="Agent"?(g="subagent",v={subagentType:o.subagent_type??null,description:typeof o.description=="string"?o.description.slice(0,200):null}):n==="WebSearch"||n==="WebFetch"?(g=n==="WebSearch"?"web_search":"web_fetch",v={query:typeof o.query=="string"?o.query.slice(0,200):null}):(_=false,g="tool_use",v={});if(_){let w={id:t.tool_use_id?`memory:${k(i,t.tool_use_id)}`:`evt:${Date.now()}:${Math.random().toString(36).slice(2,7)}`,sessionId:i,tool:n,kind:g,payload:v,command:p||void 0,exitCode:d,stderr:l??void 0,durationMs:0,createdAt:Date.now()};this.postMemoryEvent(w);}let S=this.enforcePostToolUse(n);return S?(console.error("[devflow-daemon] Enforcement:",S),{hookSpecificOutput:{hookEventName:"PostToolUse",additionalContext:JSON.stringify(S)}}):null}async handlePostToolUseFailure(e){let t;try{t=JSON.parse(e);}catch{return null}let n=this.trackSession(t.session_id),o=t.tool_name??"unknown",s=t.error??`${o} failed`,i=this.popEventFromMap(o,n,t.tool_use_id);if(i)await this.telemetry.completeEvent({eventId:i.eventId,sessionId:n,error:s,duration:Date.now()-i.timestamp,completedAt:Date.now()})&&this.noteTelemetryOperation();else {let a=k(n,t.tool_use_id);await this.telemetry.sendEvent({eventId:a,executionId:this.runtimeStore.get(n)?.executionId??n,sessionId:n,projectRoot:this.projectRoot,toolUseId:t.tool_use_id,timestamp:Date.now(),toolName:o,toolType:o.startsWith("mcp__")?"mcp":"direct",isMcpTool:o.startsWith("mcp__"),mcpToolName:o.startsWith("mcp__")?o.replace(/^mcp__[^_]+__/,""):void 0,mcpEnforced:false,mcpFallback:false,kind:"error",input:t.tool_input??{},duration:0,error:s,blocked:false});}return this.postMemoryEvent({id:t.tool_use_id?`memory:${k(n,t.tool_use_id)}`:`evt:${Date.now()}:${Math.random().toString(36).slice(2,7)}`,sessionId:n,tool:o,command:typeof t.tool_input?.command=="string"?t.tool_input.command:o,exitCode:-1,stderr:s,durationMs:i?Date.now()-i.timestamp:0,createdAt:Date.now(),kind:o==="Bash"?"bash_error":"tool_use",payload:{command:t.tool_input?.command??o,exitCode:-1,stderr:s}}),null}countResults(e){if(!e)return 0;let t=e;if(typeof e=="string")try{t=JSON.parse(e);}catch{return 1}let n=t,o=["files","results","data","result","memories","nodes","chunks","findings","symbols"],s=n.data??n.structuredContent??n;if(Array.isArray(s))return s.length;if(typeof s=="object"&&s!==null){let i=s,a=0;for(let c of o)Array.isArray(i[c])&&(a+=i[c].length);for(let[,c]of Object.entries(i))c&&typeof c=="object"&&!Array.isArray(c)&&(a+=this.countResults(c));return a>0?a:Object.keys(i).length>0?1:0}return 0}async postMemoryEvent(e){we(`/api/memory/session-events?rootPath=${encodeURIComponent(this.projectRoot)}`,e);try{await(await this.getMemoryGate()).recordEvent(e);}catch(t){console.error("[devflow-daemon] Local memory event write failed:",t.message);}}async getMemoryGate(){let e=this.explicitMemoryGate??=new MemoryGate(this.projectRoot);return this.memoryWarmup??=e.forceWarmUp().catch(t=>{throw this.memoryWarmup=null,t}),await this.memoryWarmup,e}async handleUserPromptSubmit(e){try{let t=JSON.parse(e),n=typeof t.prompt=="string"?t.prompt:"",o=typeof t.session_id=="string"&&t.session_id.trim()?this.trackSession(t.session_id):null,s=He(n);if(o&&s&&this.startSkillExecution(o,s.skillName),n.trim()){let i=$e(n);i&&Je(this.projectRoot,{content:i,sessionId:o??void 0,createdAt:Date.now()}),o&&await(await this.getMemoryGate()).recordUserMessage(n.slice(0,2e3),o,n.slice(0,200));}}catch{}return {hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow"}}}async handlePreCompact(e){let t;try{let i=JSON.parse(e);typeof i.session_id=="string"&&i.session_id.trim()&&(t=i.session_id.trim());}catch{}await this.flushTelemetry(),this.startExplicitIntentFlush(),await this.explicitIntentFlush;let n=await this.getMemoryGate(),o=await N({projectRoot:this.projectRoot,sessionId:t,trigger:"pre_compact",memory:n}),s=Y(o);return {hookSpecificOutput:{hookEventName:"PreCompact",...s?{additionalContext:s}:{}}}}noteTelemetryOperation(){this.successfulTelemetryOperations++,this.successfulTelemetryOperations>=Xn&&this.flushTelemetry();}async flushTelemetry(){this.successfulTelemetryOperations=0,await this.telemetry.flushAndAggregate();}startExplicitIntentFlush(){this.explicitIntentFlush||(this.explicitIntentFlush=this.flushExplicitMemoryIntents().finally(()=>{this.explicitIntentFlush=null;}));}async handleStop(e){let t=null;try{let n=JSON.parse(e);typeof n.session_id=="string"&&n.session_id.trim()&&(t=n.session_id.trim());}catch{}if(t??=this.activeSessionIds.size===1?[...this.activeSessionIds][0]:null,t){let n=this.runtimeStore.completeExecution(t);n?.executionId&&(await this.telemetry.sendExecutionComplete(n.executionId),this.deleteContextReceipt(t,n.executionId));}this.startExplicitIntentFlush(),await this.explicitIntentFlush;}async flushExplicitMemoryIntents(){this.flushTelemetry();let e=Ge(this.projectRoot);if(e)try{let t=this.explicitMemoryGate??=new MemoryGate(this.projectRoot);for(await t.forceWarmUp();e.intents.length>0;){let n=e.intents[0];await t.saveExplicitMemoryIntent(n.content,n.sessionId),We(e);}}catch(t){console.error("[devflow-daemon] Explicit memory intent save failed:",t.message);}}deleteContextReceipt(e,t){let n;try{n=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250}),n.deleteContextReceipt(this.projectRoot,e,t);}catch{}finally{n?.close();}}async handleMemorySnapshot(){let e=loadConfig(this.projectRoot).sessionStart?.injectMemories??true;if(e===false)return {status:"ok",enabled:false,markdown:""};let t=typeof e=="object"?e.topN:void 0,n=typeof e=="object"?e.budgetTokens:void 0,o=Number.isFinite(t)?Math.max(0,Math.floor(t)):10,s=Number.isFinite(n)?Math.max(1,Math.floor(n)):800;try{let c={...await(this.explicitMemoryGate??=new MemoryGate(this.projectRoot)).getAll({purpose:"session_bootstrap",budgetTokens:s,limit:o}),_devflow_unique:{memory_version:1,structured_storage:!0,project_context_included:!0},_accuracy:{data_freshness_ms:0,source_layer:"memory",degradation:"none"}};if(c.memories.length===0)return {status:"ok",enabled:!0,markdown:`## Project memory
|
|
10
|
-
\u672C\u9879\u76EE\u6682\u65E0\u8BB0\u5FC6`};let
|
|
11
|
-
${
|
|
9
|
+
`);let t=fstatSync(e);this.pidIdentity={dev:t.dev,ino:t.ino};}finally{closeSync(e);}}removePathIfOwned(e,t,n,o){try{return !this.pathMatchesIdentity(e,t)||o!==void 0&&readFileSync(e,"utf8")!==o||!this.pathMatchesIdentity(e,t)?!1:(unlinkSync(e),!0)}catch(s){return s.code!=="ENOENT"&&console.error(`[devflow-daemon] Failed to remove ${n}:`,s.message),false}}isSocketAcceptingConnections(){return new Promise((e,t)=>{let n=createConnection(this.socketPath),o=setTimeout(()=>{n.destroy(),t(new Error(`Timed out probing daemon socket ${this.socketPath}`));},Hn),s=i=>{clearTimeout(o),n.destroy(),e(i);};n.once("connect",()=>s(true)),n.once("error",i=>{let a=i.code;a==="ENOENT"||a==="ECONNREFUSED"?s(false):(clearTimeout(o),t(i));});})}listen(){return new Promise((e,t)=>{let n=this.server;if(!n){t(new Error("Daemon server is not initialized"));return}let o=i=>{n.off("listening",s),t(i);},s=()=>{n.off("error",o);try{let i=this.getPathIdentity(this.socketPath);if(!i)throw new Error(`Daemon socket missing after listen: ${this.socketPath}`);this.socketIdentity=i,e();}catch(i){t(i);}};n.once("error",o),n.once("listening",s),n.listen(this.socketPath);})}resetIdleTimer(){this.idleTimer&&clearTimeout(this.idleTimer),this.idleTimer=null,!(this.activeSessionIds.size>0)&&(this.idleTimer=setTimeout(()=>{console.error("[devflow-daemon] Idle timeout, exiting"),this.shutdown();},Gn).unref());}async handleRequest(e){switch(e.type){case "pre-tool-use":return this.handlePreToolUse(e.input);case "post-tool-use":return this.handlePostToolUse(e.input);case "post-tool-use-failure":return this.handlePostToolUseFailure(e.input);case "stop":return await this.handleStop(e.input??""),{status:"ok"};case "memory-snapshot":return this.handleMemorySnapshot();case "session-start":return this.handleSessionStart(e.input??"");case "session-end":return await this.handleSessionEnd(e.input??""),{status:"ok"};case "user-prompt-submit":return this.handleUserPromptSubmit(e.input);case "pre-compact":return this.handlePreCompact(e.input);default:return {error:"unknown request type"}}}async handlePreToolUse(e){try{if(!e||e.trim()==="")return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}};let t=JSON.parse(e);t.tool_input=Zn(t.tool_name,t.tool_input,process.env,this.projectRoot);let n=this.trackSession(t.session_id);if(t.tool_name==="Skill"){let g=t.tool_input?.skill,y=g?U(g):null;y&&this.startSkillExecution(n,y);}let o=this.runtimeStore.get(n);t.tool_name.startsWith("mcp__devflow__")&&(t.tool_input._devflow_session_id=n,t.tool_input._devflow_execution_id=o?.executionId??n,t.tool_use_id&&(t.tool_input._devflow_tool_use_id=t.tool_use_id));let s=t.tool_name.startsWith("mcp__"),i=s?t.tool_name.replace(/^mcp__[^_]+__/,""):void 0,a=new k(this.projectRoot,n,o?.executionId),c=a.getPhase(),u=a.evaluate(t.tool_name,s),l;u.permissionDecision==="deny"?l={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:u.reason??"MCP context required"}}:u.additionalContext?l={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",additionalContext:u.additionalContext}}:l={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}},t.tool_name.startsWith("mcp__devflow__")&&(l.hookSpecificOutput.updatedInput=t.tool_input);let m={...t.tool_input};delete m._devflow_session_id,delete m._devflow_execution_id,delete m._devflow_tool_use_id;let p=o?.executionId??n,d={eventId:D(n,t.tool_use_id),executionId:p,sessionId:n,projectRoot:this.projectRoot,toolUseId:t.tool_use_id,timestamp:Date.now(),toolName:t.tool_name,toolType:s?"mcp":t.tool_name==="Agent"?"subagent":"direct",isMcpTool:s,mcpToolName:i,mcpEnforced:s&&c==="context_gathering",mcpFallback:!s&&c==="context_gathering",kind:Qn(t.tool_name),input:m,duration:0,blocked:l&&l.hookSpecificOutput?.permissionDecision==="deny"},h=await this.telemetry.sendEvent({...d,blockReason:d.blocked?"DEVFLOW_CONTEXT_REQUIRED":void 0,error:d.blocked?"DEVFLOW_CONTEXT_REQUIRED":void 0});return h&&!d.blocked&&(this.eventMap.push({toolName:t.tool_name,toolUseId:t.tool_use_id,eventId:h,sessionId:n,timestamp:d.timestamp}),this.noteTelemetryOperation()),h&&d.blocked&&await this.telemetry.completeEvent({eventId:h,sessionId:n,error:"DEVFLOW_CONTEXT_REQUIRED",duration:Date.now()-d.timestamp,completedAt:Date.now()}),l}catch{return {hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}}}}trackSession(e){let t=e?.trim()||(this.activeSessionIds.size===1?[...this.activeSessionIds][0]:"");if(!t)throw new Error("session_id_required");return this.registerSession(t),t}registerSession(e){this.activeSessionIds.has(e)||(this.activeSessionIds.size===0&&A(this.projectRoot,{bypassCount:0}),this.activeSessionIds.add(e),this.idleTimer&&(clearTimeout(this.idleTimer),this.idleTimer=null),this.serverManager.addRef(e),this.telemetry.sendSessionStart(e,this.projectRoot,Date.now()).catch(()=>{})),this.runtimeStore.registerSession(e);}startSkillExecution(e,t){let n=this.runtimeStore.get(e)?.executionId,o=this.runtimeStore.startExecution(e,t,he(t));o.executionId&&o.executionId!==n&&this.telemetry.sendExecutionStart(o.executionId,e,t,o.lastActivityAt,this.projectRoot,o.requiredMcpTools).catch(()=>{});}async handleSessionStart(e){try{let t=JSON.parse(e);if(typeof t.session_id!="string"||!t.session_id.trim())return {error:"session_id_required"};let n=t.session_id.trim();this.registerSession(n);let o=await this.getMemoryGate();await o.ensureSession(n,this.projectRoot,"Claude Code session"),await o.releasePendingDistillLeases(n);let s=o.getPendingEventCount();return {status:"ok",...s>0?{additionalContext:Q({pendingEvents:s,releasedLeases:0,trigger:"session_end"})}:{}}}catch{return {error:"session_start_invalid"}}}popEventFromMap(e,t,n){let o=this.eventMap.filter(i=>i.toolName===e&&i.sessionId===t);if(o.length===0)return null;let s=n?o.find(i=>i.toolUseId===n):o.sort((i,a)=>i.timestamp-a.timestamp)[0];return s?(this.eventMap=this.eventMap.filter(i=>i.eventId!==s.eventId),s):null}enforcePostToolUse(e){if(!Yn[e])return null;let t=Math.floor(Date.now()/1e3),n=Y(this.projectRoot,i=>t-(i.lastMcpCall??0)<30?i:{...i,bypassCount:(i.bypassCount??0)+1});if(!n.applied||!n.changed)return null;let o=n.receipt.bypassCount??0;if(o===1)return null;let s;switch(e){case "Agent":s=`\u7B2C ${o} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u4F7F\u7528\u4E86 Agent \u5B50\u4EE3\u7406\u6267\u884C\u641C\u7D22\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "Bash":s=`\u7B2C ${o} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86 Bash \u547D\u4EE4\u641C\u7D22\u4EE3\u7801\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "Glob":s=`\u7B2C ${o} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86 Glob \u5339\u914D\u6587\u4EF6\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "Grep":s=`\u7B2C ${o} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86 Grep \u641C\u7D22\u4EE3\u7801\u3002\u4E0B\u6B21\u8BF7\u7528 get_project_context MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;case "WebSearch":case "WebFetch":s=`\u7B2C ${o} \u6B21\u8FDD\u89C4\uFF1A\u4F60\u521A\u624D\u7528\u4E86\u7F51\u7EDC\u641C\u7D22\u3002\u4E0B\u6B21\u8BF7\u7528 get_knowledge MCP \u5DE5\u5177\u66FF\u4EE3\u3002`;break;default:s=`\u7B2C ${o} \u6B21\u8FDD\u89C4\uFF1A\u8BF7\u4F7F\u7528 DevFlow MCP \u5DE5\u5177\u66FF\u4EE3\u76F4\u63A5\u8C03\u7528\u3002`;}return o>=3?s=`${s} \u5DF2\u8FBE\u5230\u6700\u5927\u8FDD\u89C4\u6B21\u6570\uFF0C\u540E\u7EED\u7ED5\u8FC7\u5C06\u88AB\u786C\u963B\u6B62\u3002`:o>=2?s=`${s} \u4E0B\u6B21\u8FDD\u89C4\u5C06\u88AB\u963B\u6B62\u3002`:s=`${s} \u8BF7\u7ACB\u5373\u7EA0\u6B63\u3002`,s}async handlePostToolUse(e){if(!e)return null;let t;try{t=JSON.parse(e);}catch{return null}let n=t.tool_name||"",o=t.tool_input||{},s=t.tool_response,i=this.trackSession(t.session_id);if(n==="Skill"){let v=o?.skill,w=v?U(v):null;w&&(this.startSkillExecution(i,w),A(this.projectRoot,{lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0}));}n.startsWith("mcp__")&&new k(this.projectRoot).recordMcpCall();let a=this.popEventFromMap(n,i,t.tool_use_id);if(a){let v=typeof s=="string"?s:JSON.stringify(s),w=v.length>5e3?{_truncated:true,_originalSize:v.length,text:v.slice(0,5e3)}:s,S=Date.now()-a.timestamp;await this.telemetry.completeEvent({eventId:a.eventId,sessionId:i,output:w,duration:S,completedAt:Date.now()})&&this.noteTelemetryOperation();}let c=typeof s=="object"&&s!==null?s:null,u=c?.exitCode,l=c?.stderr,m=/^\s*(ls|cat|pwd|cd|echo|head|tail|wc|which|whoami|date|env|printenv|id|hostname|uname)\b/,p=o.command||"",f=JSON.stringify(s),d=Buffer.byteLength(f,"utf8"),h=this.countResults(s),g,y,_=true;if(n.startsWith("mcp__"))g="mcp_call",y={mcpTool:n.replace(/^mcp__[^_]+__/,""),query:o.query??null,resultCount:h,resultSizeBytes:d};else if(n==="Read"||n==="Write"||n==="Edit")g=n==="Read"?"file_read":"file_write",y={filePath:o.file_path??null,fileContentSize:d};else if(n==="Bash"){let v=m.test(p),w=u!==void 0&&u!==0;_=!v||w,g=w?"bash_error":"bash_command",y={command:p||n,exitCode:u??null,stderr:l??null};}else n==="Agent"?(g="subagent",y={subagentType:o.subagent_type??null,description:typeof o.description=="string"?o.description.slice(0,200):null}):n==="WebSearch"||n==="WebFetch"?(g=n==="WebSearch"?"web_search":"web_fetch",y={query:typeof o.query=="string"?o.query.slice(0,200):null}):(_=false,g="tool_use",y={});if(_){let v={id:t.tool_use_id?`memory:${D(i,t.tool_use_id)}`:`evt:${Date.now()}:${Math.random().toString(36).slice(2,7)}`,sessionId:i,tool:n,kind:g,payload:y,command:p||void 0,exitCode:u,stderr:l??void 0,durationMs:0,createdAt:Date.now()};this.postMemoryEvent(v);}let E=this.enforcePostToolUse(n);return E?(console.error("[devflow-daemon] Enforcement:",E),{hookSpecificOutput:{hookEventName:"PostToolUse",additionalContext:JSON.stringify(E)}}):null}async handlePostToolUseFailure(e){let t;try{t=JSON.parse(e);}catch{return null}let n=this.trackSession(t.session_id),o=t.tool_name??"unknown",s=t.error??`${o} failed`,i=this.popEventFromMap(o,n,t.tool_use_id);if(i)await this.telemetry.completeEvent({eventId:i.eventId,sessionId:n,error:s,duration:Date.now()-i.timestamp,completedAt:Date.now()})&&this.noteTelemetryOperation();else {let a=D(n,t.tool_use_id);await this.telemetry.sendEvent({eventId:a,executionId:this.runtimeStore.get(n)?.executionId??n,sessionId:n,projectRoot:this.projectRoot,toolUseId:t.tool_use_id,timestamp:Date.now(),toolName:o,toolType:o.startsWith("mcp__")?"mcp":"direct",isMcpTool:o.startsWith("mcp__"),mcpToolName:o.startsWith("mcp__")?o.replace(/^mcp__[^_]+__/,""):void 0,mcpEnforced:false,mcpFallback:false,kind:"error",input:t.tool_input??{},duration:0,error:s,blocked:false});}return this.postMemoryEvent({id:t.tool_use_id?`memory:${D(n,t.tool_use_id)}`:`evt:${Date.now()}:${Math.random().toString(36).slice(2,7)}`,sessionId:n,tool:o,command:typeof t.tool_input?.command=="string"?t.tool_input.command:o,exitCode:-1,stderr:s,durationMs:i?Date.now()-i.timestamp:0,createdAt:Date.now(),kind:o==="Bash"?"bash_error":"tool_use",payload:{command:t.tool_input?.command??o,exitCode:-1,stderr:s}}),null}countResults(e){if(!e)return 0;let t=e;if(typeof e=="string")try{t=JSON.parse(e);}catch{return 1}let n=t,o=["files","results","data","result","memories","nodes","chunks","findings","symbols"],s=Array.isArray(n.data)?n.data:n.structuredContent??n;if(Array.isArray(s))return s.length;if(typeof s=="object"&&s!==null){let i=s,a=0,c=false;for(let u of o)Array.isArray(i[u])&&(c=true,a+=i[u].length);for(let[,u]of Object.entries(i))u&&typeof u=="object"&&!Array.isArray(u)&&(a+=this.countResults(u));return a>0?a:c?0:Object.keys(i).length>0?1:0}return 0}async postMemoryEvent(e){_e(`/api/memory/session-events?rootPath=${encodeURIComponent(this.projectRoot)}`,e);try{await(await this.getMemoryGate()).recordEvent(e);}catch(t){console.error("[devflow-daemon] Local memory event write failed:",t.message);}}async getMemoryGate(){let e=this.explicitMemoryGate??=new MemoryGate(this.projectRoot);return this.memoryWarmup??=e.forceWarmUp().catch(t=>{throw this.memoryWarmup=null,t}),await this.memoryWarmup,e}async handleUserPromptSubmit(e){try{let t=JSON.parse(e),n=typeof t.prompt=="string"?t.prompt:"",o=typeof t.session_id=="string"&&t.session_id.trim()?this.trackSession(t.session_id):null,s=Be(n);if(o&&s&&this.startSkillExecution(o,s.skillName),n.trim()){let i=qe(n);i&&Ge(this.projectRoot,{content:i,sessionId:o??void 0,createdAt:Date.now()}),o&&await(await this.getMemoryGate()).recordUserMessage(n.slice(0,2e3),o,n.slice(0,200));}}catch{}return {hookSpecificOutput:{hookEventName:"UserPromptSubmit",permissionDecision:"allow"}}}async handlePreCompact(e){let t;try{let i=JSON.parse(e);typeof i.session_id=="string"&&i.session_id.trim()&&(t=i.session_id.trim());}catch{}await this.flushTelemetry(),this.startExplicitIntentFlush(),await this.explicitIntentFlush;let n=await this.getMemoryGate(),o=await L({projectRoot:this.projectRoot,sessionId:t,trigger:"pre_compact",memory:n}),s=Q(o);return {hookSpecificOutput:{hookEventName:"PreCompact",...s?{additionalContext:s}:{}}}}noteTelemetryOperation(){this.successfulTelemetryOperations++,this.successfulTelemetryOperations>=Xn&&this.flushTelemetry();}async flushTelemetry(){this.successfulTelemetryOperations=0,await this.telemetry.flushAndAggregate();}startExplicitIntentFlush(){this.explicitIntentFlush||(this.explicitIntentFlush=this.flushExplicitMemoryIntents().finally(()=>{this.explicitIntentFlush=null;}));}async handleStop(e){let t=null;try{let n=JSON.parse(e);typeof n.session_id=="string"&&n.session_id.trim()&&(t=n.session_id.trim());}catch{}if(t??=this.activeSessionIds.size===1?[...this.activeSessionIds][0]:null,t){let n=this.runtimeStore.completeExecution(t);n?.executionId&&(await this.telemetry.sendExecutionComplete(n.executionId),this.deleteContextReceipt(t,n.executionId));}this.startExplicitIntentFlush(),await this.explicitIntentFlush;}async flushExplicitMemoryIntents(){this.flushTelemetry();let e=We(this.projectRoot);if(e)try{let t=this.explicitMemoryGate??=new MemoryGate(this.projectRoot);for(await t.forceWarmUp();e.intents.length>0;){let n=e.intents[0];await t.saveExplicitMemoryIntent(n.content,n.sessionId),He(e);}}catch(t){console.error("[devflow-daemon] Explicit memory intent save failed:",t.message);}}deleteContextReceipt(e,t){let n;try{n=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250}),n.deleteContextReceipt(this.projectRoot,e,t);}catch{}finally{n?.close();}}async handleMemorySnapshot(){let e=loadConfig(this.projectRoot).sessionStart?.injectMemories??true;if(e===false)return {status:"ok",enabled:false,markdown:""};let t=typeof e=="object"?e.topN:void 0,n=typeof e=="object"?e.budgetTokens:void 0,o=Number.isFinite(t)?Math.max(0,Math.floor(t)):10,s=Number.isFinite(n)?Math.max(1,Math.floor(n)):800;try{let i=this.explicitMemoryGate??=new MemoryGate(this.projectRoot),c={...await i.getAll({purpose:"session_bootstrap",budgetTokens:s,limit:o}),_devflow_unique:{memory_version:1,structured_storage:!0,project_context_included:!0},_accuracy:{data_freshness_ms:0,source_layer:"memory",degradation:typeof i.getObservationVectorCoverage=="function"?i.getObservationVectorCoverage().missing>0?"bm25_only":"none":"partial"}};if(c.memories.length===0)return {status:"ok",enabled:!0,markdown:`## Project memory
|
|
10
|
+
\u672C\u9879\u76EE\u6682\u65E0\u8BB0\u5FC6`};let u=c.memories.map(l=>`- **${l.title||l.type}**: ${l.content} (confidence ${l.confidence.toFixed(2)}, ${l.source})`);return {status:"ok",enabled:!0,markdown:`## Project memory (auto-injected, ${c.memories.length} items, ${c.tokenCount} tokens)
|
|
11
|
+
${u.join(`
|
|
12
12
|
`)}`}}catch(i){return console.error("[devflow-daemon] Memory snapshot failed:",i.message),{status:"degraded",enabled:true,markdown:`## Project memory
|
|
13
|
-
\u672C\u9879\u76EE\u6682\u65E0\u8BB0\u5FC6`}}}async handleSessionEnd(e){let t=null;try{let i=JSON.parse(e);typeof i.session_id=="string"&&i.session_id.trim()&&(t=i.session_id.trim());}catch{}t??=this.activeSessionIds.size===1?[...this.activeSessionIds][0]:null;let n=t===null?this.activeSessionIds.size<=1:this.activeSessionIds.size<=1&&this.activeSessionIds.has(t);this.startExplicitIntentFlush(),await this.explicitIntentFlush;let o=await this.getMemoryGate(),s=t?this.runtimeStore.get(t)?.executionId:void 0;s&&await this.telemetry.sendExecutionComplete(s),t&&this.deleteContextReceipt(t,s),await
|
|
14
|
-
`);}};if(process.env.DEVFLOW_HOOK_DAEMON_DISABLE_AUTOSTART!=="1"){let r=process.argv[2]||process.env.CLAUDE_PROJECT_DIR||process.cwd(),e=new
|
|
13
|
+
\u672C\u9879\u76EE\u6682\u65E0\u8BB0\u5FC6`}}}async handleSessionEnd(e){let t=null;try{let i=JSON.parse(e);typeof i.session_id=="string"&&i.session_id.trim()&&(t=i.session_id.trim());}catch{}t??=this.activeSessionIds.size===1?[...this.activeSessionIds][0]:null;let n=t===null?this.activeSessionIds.size<=1:this.activeSessionIds.size<=1&&this.activeSessionIds.has(t);this.startExplicitIntentFlush(),await this.explicitIntentFlush;let o=await this.getMemoryGate(),s=t?this.runtimeStore.get(t)?.executionId:void 0;s&&await this.telemetry.sendExecutionComplete(s),t&&this.deleteContextReceipt(t,s),await Z(e,this.projectRoot,n,{telemetry:this.telemetry,memory:o}),t&&(this.serverManager.removeRef(t),this.activeSessionIds.delete(t),this.eventMap=this.eventMap.filter(i=>i.sessionId!==t),this.runtimeStore.removeSession(t)),this.activeSessionIds.size===0?setImmediate(()=>{this.shutdown();}):this.resetIdleTimer();}shutdown(){return this.shutdownPromise?this.shutdownPromise:(this.shutdownPromise=this.performShutdown(),this.shutdownPromise)}async performShutdown(){this.idleTimer&&(clearTimeout(this.idleTimer),this.idleTimer=null),this.telemetryFlushTimer&&(clearInterval(this.telemetryFlushTimer),this.telemetryFlushTimer=null),await this.flushTelemetry(),await this.closeServer(),this.cleanupOwnedResources(),await this.serverManager.stop(),await this.explicitIntentFlush,this.explicitMemoryGate?.close(),this.explicitMemoryGate=null,this.telemetry.close(),process.exit(0);}closeServer(){let e=this.server;return !e||!e.listening||!this.pathMatchesIdentity(this.socketPath,this.socketIdentity)?Promise.resolve():(this.server=null,new Promise(t=>{let n=false,o=()=>{n||(n=true,clearTimeout(s),t());},s=setTimeout(o,Bn);e.close(o);}))}cleanupOwnedResources(){try{Ne(this.projectRoot,process.pid);}catch(e){console.error("[devflow-daemon] Failed to unregister:",e.message);}this.removePathIfOwned(this.socketPath,this.socketIdentity,"socket"),this.removePathIfOwned(this.pidPath,this.pidIdentity,"PID file",`${process.pid}
|
|
14
|
+
`);}};if(process.env.DEVFLOW_HOOK_DAEMON_DISABLE_AUTOSTART!=="1"){let r=process.argv[2]||process.env.CLAUDE_PROJECT_DIR||process.cwd(),e=new re(r,process.argv[3]);process.once("SIGTERM",()=>{e.shutdown();}),process.once("SIGINT",()=>{e.shutdown();}),e.start().catch(t=>{console.error("[devflow-daemon] Failed to start:",t),process.exit(1);});}export{re as HookDaemon,Zn as injectProjectRootIfMissing};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {request}from'http';import {getLocalApiKey}from'@devflow-tools/sdk/auth';import {classifyWorkflowFailure}from'@devflow-tools/workflow-engine/failure-classifier';import {existsSync,readFileSync,rmSync,mkdirSync,writeFileSync,renameSync,readdirSync,unlinkSync}from'fs';import {join}from'path';import {homedir}from'os';import {openGlobalDevFlowDatabase}from'@devflow-tools/database';import {getLocalApiKey as getLocalApiKey$1,getProjectStateDir}from'@devflow-tools/sdk';import {randomUUID,createHash}from'crypto';var S=1e4,L=3e4,h=500;function N(i,t,e){return new Promise(r=>{try{let n=new URL(i),s=request({hostname:n.hostname,port:n.port||80,path:n.pathname+n.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":e,"Content-Length":Buffer.byteLength(t)},timeout:5e3},o=>{let a=[];o.on("data",c=>a.push(c)),o.on("end",()=>{let c=Buffer.concat(a).toString();r(o.statusCode!=null&&o.statusCode>=200&&o.statusCode<300?c:null);});});s.on("error",()=>r(null)),s.on("timeout",()=>{s.destroy(),r(null);}),s.write(t),s.end();}catch{r(null);}})}var g=class{constructor(t){this.retryScheduled=false;this.apiUrl=t?.apiUrl??process.env.DEVFLOW_API_URL??"http://127.0.0.1:13337",this.apiKey=t?.apiKey??getLocalApiKey$1(),this.cacheDir=t?.cacheDir??join(process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow","global"),"telemetry-cache"),this.legacyCacheDir=t?.legacyCacheDir??(t?.cacheDir?null:join(homedir(),".devflow","telemetry-cache")),this.database=t?.database??null,this.ownsDatabase=!t?.database;}async sendEvent(t){let e={...t,input:this.truncateInput(t.input)};return this.commitOrCache("tool_call",e)?(this.postHttp("/api/telemetry/tool-call",e),t.eventId):null}async sendExecutionStart(t,e,r,n,s=process.env.CLAUDE_PROJECT_DIR??process.cwd(),o=[]){let a={executionId:t,sessionId:e,skillName:r,startedAt:n,projectRoot:s,requiredMcpTools:o};this.commitOrCache("execution_start",a)&&this.postHttp("/api/telemetry/skill-execution/start",a);}async sendExecutionComplete(t,e="completed",r=Date.now(),n){let s={executionId:t,status:e,finishedAt:r,metadata:n},o=this.commitOrCache("execution_complete",s);return o&&this.postHttp("/api/telemetry/skill-execution/complete-reconciled",s),o}async sendSessionStart(t,e,r){let n={id:t,projectRoot:e,startedAt:r,label:e.split("/").pop()??"unknown"},s=this.commitOrCache("session_start",n);return s&&this.postHttp("/api/telemetry/sessions",n),s}async completeEvent(t){let e=this.commitOrCache("complete_event",t);return e&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp("/api/telemetry/tool-call/output",t)),e}async endSession(t,e=Date.now()){let r={sessionId:t,finishedAt:e},n=this.commitOrCache("session_end",r);return n&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp(`/api/telemetry/sessions/${encodeURIComponent(t)}/close`,{finishedAt:e})),n}async flushCache(){try{let r=this.getDatabase(),n=r.listTelemetryFailures({unresolvedOnly:!0,limit:h}).reverse();for(let s of n)try{this.applyOperation(s.operation,s.payload),r.resolveTelemetryFailure(s.id);}catch{}r.trimTelemetryFailures(h);}catch{}let e=[...new Set([this.cacheDir,this.legacyCacheDir].filter(r=>!!r))].filter(r=>existsSync(r)).flatMap(r=>readdirSync(r).filter(n=>n.endsWith(".json")).map(n=>{let s=join(r,n);try{return {cacheFile:s,envelope:$(JSON.parse(readFileSync(s,"utf8")),n)}}catch{return null}})).filter(r=>r!==null).sort((r,n)=>r.envelope.timestamp-n.envelope.timestamp||T(r.envelope.operation)-T(n.envelope.operation));for(let{cacheFile:r,envelope:n}of e)try{let s=this.getDatabase();s.insertTelemetryFailure({id:n.id,operation:n.operation,payload:n.payload,error:n.failure,createdAt:n.timestamp}),this.applyOperation(n.operation,n.payload),s.resolveTelemetryFailure(n.id),unlinkSync(r);}catch{}this.database?.aggregatePendingToolMetrics();}aggregateMetrics(){return this.getDatabase().aggregatePendingToolMetrics()}async flushAndAggregate(){await this.flushCache(),this.aggregateMetrics();}close(){this.ownsDatabase&&this.database?.close(),this.database=null;}getDatabase(){return this.database??=openGlobalDevFlowDatabase(),this.database}commitOrCache(t,e){try{return this.applyOperation(t,e),!0}catch(r){return this.cacheOperation(t,e,r),false}}applyOperation(t,e){let r=this.getDatabase();switch(t){case "tool_call":this.ensureParentSession(r,e),r.insertToolCallEvent(e);return;case "execution_start":this.ensureParentSession(r,e),r.insertSkillExecution({executionId:e.executionId,sessionId:e.sessionId,skillName:e.skillName,startedAt:e.startedAt,status:"running"});return;case "execution_complete":r.reconcileSkillExecution(e.executionId,e.status,e.finishedAt,e.metadata);return;case "session_start":r.ensureSession(e),r.insertRun({id:R(e.id),source:"hook",tool:"session",input:{projectRoot:e.projectRoot},status:"active",startedAt:e.startedAt,tokenUsed:0,metadata:{sessionId:e.id,projectRoot:e.projectRoot}});return;case "complete_event":if(!r.updateToolCallEvent(e.eventId,{output:e.output===void 0?void 0:JSON.stringify(e.output),error:e.error,duration:e.duration}))throw new Error(`Tool call event ${e.eventId} is not available for completion`);return;case "session_end":r.closeSession(e.sessionId,e.finishedAt),r.updateRun(R(e.sessionId),{status:"completed",finishedAt:e.finishedAt});return}}ensureParentSession(t,e){let r=typeof e.sessionId=="string"?e.sessionId.trim():"";if(!r)throw new Error("Canonical session ID is required for telemetry");let n=typeof e.projectRoot=="string"&&e.projectRoot.trim()?e.projectRoot:typeof e.input?.projectRoot=="string"&&e.input.projectRoot.trim()?e.input.projectRoot:process.env.CLAUDE_PROJECT_DIR??"unknown";t.ensureSession({id:r,projectRoot:n,label:n==="unknown"?void 0:n.split("/").pop(),startedAt:Number(e.startedAt??e.timestamp??Date.now())});}cacheOperation(t,e,r){let n=Date.now(),s={id:`failure:${n}:${Math.random().toString(36).slice(2,11)}`,operation:t,payload:e,failure:r instanceof Error?r.message:String(r),timestamp:n};try{let o=this.getDatabase();o.insertTelemetryFailure({id:s.id,operation:t,payload:e,error:s.failure,createdAt:n}),o.trimTelemetryFailures(h),this.scheduleRetry();return}catch{}try{existsSync(this.cacheDir)||mkdirSync(this.cacheDir,{recursive:!0});let o=join(this.cacheDir,`${n}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync(o,JSON.stringify(s,null,2),{mode:384}),this.trimCompatibilityCache(),this.scheduleRetry();}catch{}}trimCompatibilityCache(){try{let t=readdirSync(this.cacheDir).filter(e=>e.endsWith(".json")).sort();for(let e of t.slice(0,Math.max(0,t.length-h)))unlinkSync(join(this.cacheDir,e));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},L).unref());}postHttp(t,e){N(`${this.apiUrl}${t}`,JSON.stringify(e),this.apiKey);}truncateInput(t){let e=JSON.stringify(t);return e===void 0||e.length<=S?t:{_truncated:true,_original_size:e.length,_preview:`${e.substring(0,S)}...`}}};function T(i){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(i)}function R(i){return `hook-run:${i}`}function $(i,t){if(!i||typeof i!="object")throw new Error("Invalid telemetry cache envelope");let e=i;if(typeof e.operation=="string"&&e.payload!==void 0)return e;let r=e.type==="tool_call"?"tool_call":e.type==="execution_start"?"execution_start":null;if(!r)throw new Error("Unknown legacy telemetry cache operation");let n=e.payload??{},s=Number(n.timestamp??n.startedAt??Date.now());return {id:`legacy-cache:${t}`,operation:r,payload:n,failure:"Replayed legacy HTTP-first telemetry cache entry",timestamp:s}}function J(i){return i??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function I(i){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(J(i))}function Y(i){return Buffer.from(i,"utf8").toString("base64url")}var f=class{constructor(t){this.projectRoot=t;this.sessions=new Map;this.sessionsDir=join(I(t),"hook-sessions");}registerSession(t){let e=t.trim();if(!e)throw new Error("session_id_required");let r=this.get(e);if(r)return r.lastActivityAt=Date.now(),this.persist(r),r;let n=Date.now(),s={sessionId:e,projectRoot:this.projectRoot,requiredMcpTools:[],startedAt:n,lastActivityAt:n};return this.sessions.set(e,s),this.persist(s),s}startExecution(t,e,r){let n=this.registerSession(t);return (!n.executionId||n.skillName!==e)&&(n.executionId=`exec_${Date.now()}_${randomUUID().slice(0,8)}`),n.skillName=e,n.requiredMcpTools=[...new Set(r)],n.lastActivityAt=Date.now(),this.persist(n),n}get(t){let e=t.trim();if(!e)return null;let r=this.sessions.get(e);if(r)return r;let n=this.snapshotPath(e);if(!existsSync(n))return null;try{let s=JSON.parse(readFileSync(n,"utf8"));return s.sessionId!==e||s.projectRoot!==this.projectRoot?null:(s.requiredMcpTools=Array.isArray(s.requiredMcpTools)?s.requiredMcpTools:[],this.sessions.set(e,s),s)}catch{return null}}completeExecution(t){let e=this.get(t);if(!e)return null;let r={...e,requiredMcpTools:[...e.requiredMcpTools]};return delete e.executionId,delete e.skillName,e.requiredMcpTools=[],e.lastActivityAt=Date.now(),this.persist(e),r}removeSession(t){let e=t.trim();e&&(this.sessions.delete(e),rmSync(this.snapshotPath(e),{force:true}));}list(){return [...this.sessions.values()]}snapshotPath(t){return join(this.sessionsDir,`${Y(t)}.json`)}persist(t){mkdirSync(this.sessionsDir,{recursive:true,mode:448});let e=this.snapshotPath(t.sessionId),r=`${e}.${process.pid}.tmp`;writeFileSync(r,JSON.stringify(t),{mode:384}),renameSync(r,e);}};function P(i,t){return t?`evt_tool_${createHash("sha256").update(`${i}\0${t}`).digest("hex").slice(0,24)}`:`evt_${Date.now()}_${Math.random().toString(36).slice(2,11)}`}var te=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",re=getLocalApiKey(),_=process.env.CLAUDE_PROJECT_DIR||process.cwd();function v(i,t){let e=Number(i);return Number.isInteger(e)&&e>=0?e:t}function ne(i,t,e){return new Promise((r,n)=>{let s=JSON.stringify(t),o=new URL(i,te),a=false,c=l=>{a||(a=true,l?n(l):r());},u=request({hostname:o.hostname,port:o.port,path:o.pathname+o.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":re,"Content-Length":Buffer.byteLength(s)},timeout:e},l=>{l.on("error",c),l.on("end",()=>{let m=l.statusCode??0;if(m<200||m>=300){c(new Error(`HTTP ${m}`));return}c();}),l.resume();});u.on("error",c),u.on("timeout",()=>{u.destroy(new Error(`PostToolUseFailure request timed out after ${e}ms`));}),u.write(s),u.end();})}function se(i){return new Promise(t=>setTimeout(t,i))}async function oe(i,t){let e=t.timeoutMs??v(process.env.DEVFLOW_FAILURE_TIMEOUT_MS,1500),r=t.maxRetries??v(process.env.DEVFLOW_FAILURE_MAX_RETRIES,2),n=t.retryDelayMs??v(process.env.DEVFLOW_FAILURE_RETRY_DELAY_MS,100),s=`/api/memory/session-events?rootPath=${encodeURIComponent(_)}`;for(let o=0;o<=r;o+=1)try{await ne(s,i,e);return}catch(a){if(o===r)throw a;await se(n*2**o);}}async function ie(i,t={}){let e;try{e=JSON.parse(i);}catch{return}let r=e.tool_name||"",n=e.tool_input||{},s=e.error||"",o=e.session_id?.trim();if(!o)return;let a=new f(_).registerSession(o),c=P(o,e.tool_use_id),u=classifyWorkflowFailure({stepId:r,toolName:r,error:new Error(s||`${r||"tool"} failed`),errorStack:s||void 0}),l=typeof n=="object"&&n.command?n.command:r,m={id:`memory:${c}`,sessionId:o,tool:r,command:l,exitCode:-1,stderr:s,durationMs:0,createdAt:Date.now(),kind:r==="Bash"?"bash_error":"tool_use",payload:{command:l,exitCode:-1,stderr:s,failureSource:"PostToolUseFailure",failureCategory:u.category}},O=!t.telemetryClient,y=t.telemetryClient??new g,p=r.startsWith("mcp__"),x={eventId:c,executionId:a.executionId??o,sessionId:o,projectRoot:_,toolUseId:e.tool_use_id,timestamp:m.createdAt,toolName:r,toolType:p?"mcp":"direct",isMcpTool:p,mcpToolName:p?r:void 0,mcpEnforced:p,mcpFallback:false,kind:"error",input:n,duration:0,error:s,blocked:false,failureCategory:u.category};try{await y.sendEvent(x),await y.completeEvent({eventId:c,sessionId:o,error:s,duration:0,completedAt:Date.now()});}finally{O&&y.close();}t.eventDelivery?await t.eventDelivery(m):await oe(m,t);}if(process.argv[1]?.endsWith("post-tool-use-failure")||process.argv[1]?.endsWith("post-tool-use-failure.js")){let i="";process.stdin.setEncoding("utf8"),process.stdin.on("data",t=>{i+=t;}),process.stdin.on("end",()=>{ie(i.trim()||process.argv[2]||"").catch(t=>{console.error("[devflow] PostToolUseFailure delivery failed:",t.message),process.exitCode=1;});}),process.stdin.on("error",t=>{console.error("[devflow] PostToolUseFailure input failed:",t.message),process.exitCode=1;});}
|
|
1
|
+
import {request}from'http';import {getLocalApiKey}from'@devflow-tools/sdk/auth';import {classifyWorkflowFailure}from'@devflow-tools/workflow-engine/failure-classifier';import {existsSync,readFileSync,rmSync,mkdirSync,writeFileSync,renameSync,readdirSync,unlinkSync}from'fs';import {join}from'path';import {homedir}from'os';import {openGlobalDevFlowDatabase}from'@devflow-tools/database';import {getLocalApiKey as getLocalApiKey$1,getProjectStateDir}from'@devflow-tools/sdk';import {randomUUID,createHash}from'crypto';var b=1e4,L=3e4,h=500;function N(i,t,e){return new Promise(r=>{try{let s=new URL(i),n=request({hostname:s.hostname,port:s.port||80,path:s.pathname+s.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":e,"Content-Length":Buffer.byteLength(t)},timeout:5e3},o=>{let a=[];o.on("data",c=>a.push(c)),o.on("end",()=>{let c=Buffer.concat(a).toString();r(o.statusCode!=null&&o.statusCode>=200&&o.statusCode<300?c:null);});});n.on("error",()=>r(null)),n.on("timeout",()=>{n.destroy(),r(null);}),n.write(t),n.end();}catch{r(null);}})}var g=class{constructor(t){this.retryScheduled=false;this.metricAggregationScheduled=false;this.apiUrl=t?.apiUrl??process.env.DEVFLOW_API_URL??"http://127.0.0.1:13337",this.apiKey=t?.apiKey??getLocalApiKey$1(),this.cacheDir=t?.cacheDir??join(process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow","global"),"telemetry-cache"),this.legacyCacheDir=t?.legacyCacheDir??(t?.cacheDir?null:join(homedir(),".devflow","telemetry-cache")),this.database=t?.database??null,this.ownsDatabase=!t?.database;}async sendEvent(t){let e={...t,input:this.truncateInput(t.input)};return this.commitOrCache("tool_call",e)?(this.postHttp("/api/telemetry/tool-call",e),t.eventId):null}async sendExecutionStart(t,e,r,s,n=process.env.CLAUDE_PROJECT_DIR??process.cwd(),o=[]){let a={executionId:t,sessionId:e,skillName:r,startedAt:s,projectRoot:n,requiredMcpTools:o};this.commitOrCache("execution_start",a)&&this.postHttp("/api/telemetry/skill-execution/start",a);}async sendExecutionComplete(t,e="completed",r=Date.now(),s){let n={executionId:t,status:e,finishedAt:r,metadata:s},o=this.commitOrCache("execution_complete",n);return o&&this.postHttp("/api/telemetry/skill-execution/complete-reconciled",n),o}async sendSessionStart(t,e,r){let s={id:t,projectRoot:e,startedAt:r,label:e.split("/").pop()??"unknown"},n=this.commitOrCache("session_start",s);return n&&this.postHttp("/api/telemetry/sessions",s),n}async completeEvent(t){let e=this.commitOrCache("complete_event",t);return e&&(this.postHttp("/api/telemetry/tool-call/output",t),this.scheduleMetricAggregation()),e}async endSession(t,e=Date.now()){let r={sessionId:t,finishedAt:e},s=this.commitOrCache("session_end",r);return s&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp(`/api/telemetry/sessions/${encodeURIComponent(t)}/close`,{finishedAt:e})),s}async flushCache(){try{let r=this.getDatabase(),s=r.listTelemetryFailures({unresolvedOnly:!0,limit:h}).reverse();for(let n of s)try{this.applyOperation(n.operation,n.payload),r.resolveTelemetryFailure(n.id);}catch{}r.trimTelemetryFailures(h);}catch{}let e=[...new Set([this.cacheDir,this.legacyCacheDir].filter(r=>!!r))].filter(r=>existsSync(r)).flatMap(r=>readdirSync(r).filter(s=>s.endsWith(".json")).map(s=>{let n=join(r,s);try{return {cacheFile:n,envelope:$(JSON.parse(readFileSync(n,"utf8")),s)}}catch{return null}})).filter(r=>r!==null).sort((r,s)=>r.envelope.timestamp-s.envelope.timestamp||T(r.envelope.operation)-T(s.envelope.operation));for(let{cacheFile:r,envelope:s}of e)try{let n=this.getDatabase();n.insertTelemetryFailure({id:s.id,operation:s.operation,payload:s.payload,error:s.failure,createdAt:s.timestamp}),this.applyOperation(s.operation,s.payload),n.resolveTelemetryFailure(s.id),unlinkSync(r);}catch{}this.database?.aggregatePendingToolMetrics();}aggregateMetrics(){return this.getDatabase().aggregatePendingToolMetrics()}async flushAndAggregate(){await this.flushCache(),this.aggregateMetrics();}close(){this.ownsDatabase&&this.database?.close(),this.database=null;}getDatabase(){return this.database??=openGlobalDevFlowDatabase(),this.database}commitOrCache(t,e){try{return this.applyOperation(t,e),!0}catch(r){return this.cacheOperation(t,e,r),false}}applyOperation(t,e){let r=this.getDatabase();switch(t){case "tool_call":this.ensureParentSession(r,e),r.insertToolCallEvent(e);return;case "execution_start":this.ensureParentSession(r,e),r.insertSkillExecution({executionId:e.executionId,sessionId:e.sessionId,skillName:e.skillName,startedAt:e.startedAt,status:"running",requiredMcpTools:e.requiredMcpTools});return;case "execution_complete":r.reconcileSkillExecution(e.executionId,e.status,e.finishedAt,e.metadata);return;case "session_start":r.ensureSession(e),r.insertRun({id:R(e.id),source:"hook",tool:"session",input:{projectRoot:e.projectRoot},status:"active",startedAt:e.startedAt,tokenUsed:0,metadata:{sessionId:e.id,projectRoot:e.projectRoot}});return;case "complete_event":if(!r.updateToolCallEvent(e.eventId,{output:e.output===void 0?void 0:JSON.stringify(e.output),error:e.error,duration:e.duration}))throw new Error(`Tool call event ${e.eventId} is not available for completion`);return;case "session_end":r.closeSession(e.sessionId,e.finishedAt),r.updateRun(R(e.sessionId),{status:"completed",finishedAt:e.finishedAt});return}}ensureParentSession(t,e){let r=typeof e.sessionId=="string"?e.sessionId.trim():"";if(!r)throw new Error("Canonical session ID is required for telemetry");let s=typeof e.projectRoot=="string"&&e.projectRoot.trim()?e.projectRoot:typeof e.input?.projectRoot=="string"&&e.input.projectRoot.trim()?e.input.projectRoot:process.env.CLAUDE_PROJECT_DIR??"unknown";t.ensureSession({id:r,projectRoot:s,label:s==="unknown"?void 0:s.split("/").pop(),startedAt:Number(e.startedAt??e.timestamp??Date.now())});}cacheOperation(t,e,r){let s=Date.now(),n={id:`failure:${s}:${Math.random().toString(36).slice(2,11)}`,operation:t,payload:e,failure:r instanceof Error?r.message:String(r),timestamp:s};try{let o=this.getDatabase();o.insertTelemetryFailure({id:n.id,operation:t,payload:e,error:n.failure,createdAt:s}),o.trimTelemetryFailures(h),this.scheduleRetry();return}catch{}try{existsSync(this.cacheDir)||mkdirSync(this.cacheDir,{recursive:!0});let o=join(this.cacheDir,`${s}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync(o,JSON.stringify(n,null,2),{mode:384}),this.trimCompatibilityCache(),this.scheduleRetry();}catch{}}trimCompatibilityCache(){try{let t=readdirSync(this.cacheDir).filter(e=>e.endsWith(".json")).sort();for(let e of t.slice(0,Math.max(0,t.length-h)))unlinkSync(join(this.cacheDir,e));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},L).unref());}scheduleMetricAggregation(){if(this.metricAggregationScheduled||process.env.DEVFLOW_HOOK_DEGRADED==="1")return;this.metricAggregationScheduled=true,setTimeout(()=>{this.metricAggregationScheduled=false;try{this.getDatabase().aggregatePendingToolMetrics(50)===50&&this.scheduleMetricAggregation();}catch{}},25).unref();}postHttp(t,e){N(`${this.apiUrl}${t}`,JSON.stringify(e),this.apiKey);}truncateInput(t){let e=JSON.stringify(t);return e===void 0||e.length<=b?t:{_truncated:true,_original_size:e.length,_preview:`${e.substring(0,b)}...`}}};function T(i){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(i)}function R(i){return `hook-run:${i}`}function $(i,t){if(!i||typeof i!="object")throw new Error("Invalid telemetry cache envelope");let e=i;if(typeof e.operation=="string"&&e.payload!==void 0)return e;let r=e.type==="tool_call"?"tool_call":e.type==="execution_start"?"execution_start":null;if(!r)throw new Error("Unknown legacy telemetry cache operation");let s=e.payload??{},n=Number(s.timestamp??s.startedAt??Date.now());return {id:`legacy-cache:${t}`,operation:r,payload:s,failure:"Replayed legacy HTTP-first telemetry cache entry",timestamp:n}}function J(i){return i??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function I(i){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(J(i))}function Y(i){return Buffer.from(i,"utf8").toString("base64url")}var f=class{constructor(t){this.projectRoot=t;this.sessions=new Map;this.sessionsDir=join(I(t),"hook-sessions");}registerSession(t){let e=t.trim();if(!e)throw new Error("session_id_required");let r=this.get(e);if(r)return r.lastActivityAt=Date.now(),this.persist(r),r;let s=Date.now(),n={sessionId:e,projectRoot:this.projectRoot,requiredMcpTools:[],startedAt:s,lastActivityAt:s};return this.sessions.set(e,n),this.persist(n),n}startExecution(t,e,r){let s=this.registerSession(t);return (!s.executionId||s.skillName!==e)&&(s.executionId=`exec_${Date.now()}_${randomUUID().slice(0,8)}`),s.skillName=e,s.requiredMcpTools=[...new Set(r)],s.lastActivityAt=Date.now(),this.persist(s),s}get(t){let e=t.trim();if(!e)return null;let r=this.sessions.get(e);if(r)return r;let s=this.snapshotPath(e);if(!existsSync(s))return null;try{let n=JSON.parse(readFileSync(s,"utf8"));return n.sessionId!==e||n.projectRoot!==this.projectRoot?null:(n.requiredMcpTools=Array.isArray(n.requiredMcpTools)?n.requiredMcpTools:[],this.sessions.set(e,n),n)}catch{return null}}completeExecution(t){let e=this.get(t);if(!e)return null;let r={...e,requiredMcpTools:[...e.requiredMcpTools]};return delete e.executionId,delete e.skillName,e.requiredMcpTools=[],e.lastActivityAt=Date.now(),this.persist(e),r}removeSession(t){let e=t.trim();e&&(this.sessions.delete(e),rmSync(this.snapshotPath(e),{force:true}));}list(){return [...this.sessions.values()]}snapshotPath(t){return join(this.sessionsDir,`${Y(t)}.json`)}persist(t){mkdirSync(this.sessionsDir,{recursive:true,mode:448});let e=this.snapshotPath(t.sessionId),r=`${e}.${process.pid}.tmp`;writeFileSync(r,JSON.stringify(t),{mode:384}),renameSync(r,e);}};function P(i,t){return t?`evt_tool_${createHash("sha256").update(`${i}\0${t}`).digest("hex").slice(0,24)}`:`evt_${Date.now()}_${Math.random().toString(36).slice(2,11)}`}var te=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",re=getLocalApiKey(),_=process.env.CLAUDE_PROJECT_DIR||process.cwd();function v(i,t){let e=Number(i);return Number.isInteger(e)&&e>=0?e:t}function se(i,t,e){return new Promise((r,s)=>{let n=JSON.stringify(t),o=new URL(i,te),a=false,c=l=>{a||(a=true,l?s(l):r());},u=request({hostname:o.hostname,port:o.port,path:o.pathname+o.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":re,"Content-Length":Buffer.byteLength(n)},timeout:e},l=>{l.on("error",c),l.on("end",()=>{let d=l.statusCode??0;if(d<200||d>=300){c(new Error(`HTTP ${d}`));return}c();}),l.resume();});u.on("error",c),u.on("timeout",()=>{u.destroy(new Error(`PostToolUseFailure request timed out after ${e}ms`));}),u.write(n),u.end();})}function ne(i){return new Promise(t=>setTimeout(t,i))}async function oe(i,t){let e=t.timeoutMs??v(process.env.DEVFLOW_FAILURE_TIMEOUT_MS,1500),r=t.maxRetries??v(process.env.DEVFLOW_FAILURE_MAX_RETRIES,2),s=t.retryDelayMs??v(process.env.DEVFLOW_FAILURE_RETRY_DELAY_MS,100),n=`/api/memory/session-events?rootPath=${encodeURIComponent(_)}`;for(let o=0;o<=r;o+=1)try{await se(n,i,e);return}catch(a){if(o===r)throw a;await ne(s*2**o);}}async function ie(i,t={}){let e;try{e=JSON.parse(i);}catch{return}let r=e.tool_name||"",s=e.tool_input||{},n=e.error||"",o=e.session_id?.trim();if(!o)return;let a=new f(_).registerSession(o),c=P(o,e.tool_use_id),u=classifyWorkflowFailure({stepId:r,toolName:r,error:new Error(n||`${r||"tool"} failed`),errorStack:n||void 0}),l=typeof s=="object"&&s.command?s.command:r,d={id:`memory:${c}`,sessionId:o,tool:r,command:l,exitCode:-1,stderr:n,durationMs:0,createdAt:Date.now(),kind:r==="Bash"?"bash_error":"tool_use",payload:{command:l,exitCode:-1,stderr:n,failureSource:"PostToolUseFailure",failureCategory:u.category}},A=!t.telemetryClient,y=t.telemetryClient??new g,p=r.startsWith("mcp__"),O={eventId:c,executionId:a.executionId??o,sessionId:o,projectRoot:_,toolUseId:e.tool_use_id,timestamp:d.createdAt,toolName:r,toolType:p?"mcp":"direct",isMcpTool:p,mcpToolName:p?r:void 0,mcpEnforced:p,mcpFallback:false,kind:"error",input:s,duration:0,error:n,blocked:false,failureCategory:u.category};try{await y.sendEvent(O),await y.completeEvent({eventId:c,sessionId:o,error:n,duration:0,completedAt:Date.now()});}finally{A&&y.close();}t.eventDelivery?await t.eventDelivery(d):await oe(d,t);}if(process.argv[1]?.endsWith("post-tool-use-failure")||process.argv[1]?.endsWith("post-tool-use-failure.js")){let i="";process.stdin.setEncoding("utf8"),process.stdin.on("data",t=>{i+=t;}),process.stdin.on("end",()=>{ie(i.trim()||process.argv[2]||"").catch(t=>{console.error("[devflow] PostToolUseFailure delivery failed:",t.message),process.exitCode=1;});}),process.stdin.on("error",t=>{console.error("[devflow] PostToolUseFailure input failed:",t.message),process.exitCode=1;});}
|
|
2
2
|
export{ie as handlePostToolUseFailure};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {existsSync,readFileSync,rmSync,mkdirSync,writeFileSync,renameSync,readdirSync,unlinkSync,appendFileSync}from'fs';import {join,dirname}from'path';import {getLogWriter}from'@devflow-tools/telemetry';import {request}from'http';import {homedir}from'os';import {getLocalApiKey,getProjectStateDir}from'@devflow-tools/sdk';import {openGlobalDevFlowDatabase}from'@devflow-tools/database';import {
|
|
2
|
-
`;appendFileSync(join(O,"http-errors.log"),e);}catch{}}function z(o,t,e={}){let n=e.timeout??5e3,r=e.maxRetries??2,s=e.circuitBreaker??true,i=Se(o);if(s&&i.openUntil>Date.now())return Promise.resolve();s&&i.status==="open"&&j(o,i,"half_open");let c=l=>new Promise(a=>{let w=JSON.stringify(t),d=new URL(o,F),m=request({hostname:d.hostname,port:d.port,path:d.pathname+d.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":we,"Content-Length":Buffer.byteLength(w)},timeout:n},f=>{if(f.resume(),f.statusCode&&f.statusCode>=400){let h=new Error(`HTTP ${f.statusCode}`);if(J(o,h),l>0){let b=Math.min(1e3*Math.pow(2,r-l),8e3);setTimeout(()=>{c(l-1).then(a);},b);}else A(o,i),a();return}Re(o,i),a();});m.on("error",f=>{if(J(o,f),l>0){let h=Math.min(1e3*Math.pow(2,r-l),8e3);setTimeout(()=>{c(l-1).then(a);},h);}else A(o,i),a();}),m.on("timeout",()=>{m.destroy(),l>0?c(l-1).then(a):(A(o,i),a());}),m.write(w),m.end();});return c(r)}function M(o){return o??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function g(o){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(M(o))}function R(o){if(!o||typeof o!="object"||Array.isArray(o))return {};let t=o,e={};for(let n of ["lastMcpCall","bypassCount"])if(n in t){let r=t[n];if(r==null)continue;e[n]=typeof r=="number"&&Number.isFinite(r)&&r>=0?r:0;}return e}function Ee(o,t){return o.lastMcpCall===t.lastMcpCall&&o.bypassCount===t.bypassCount}function ke(o){try{return existsSync(o)?R(JSON.parse(readFileSync(o,"utf-8"))):{}}catch{return {}}}function Ie(o,t){let e=t instanceof Error?t.message:String(t);console.error(`[devflow] Receipt ${o} skipped: ${e}`);}function B(o){let t=g(o);for(let e of ["receipt.json","receipt-lock.sqlite","receipt-lock.sqlite-shm","receipt-lock.sqlite-wal"])try{existsSync(join(t,e))&&unlinkSync(join(t,e));}catch{}}function Te(o,t){if(o.getHookReceipt(t)){B(t);return}let e=join(g(t),"receipt.json");if(!existsSync(e))return;let n=ke(e);o.updateHookReceipt(t,()=>n),B(t);}function Y(o,t,e){let n;try{n=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250}),Te(n,o);let r={},s=n.updateHookReceipt(o,c=>(r=R(c),R(t(r)))),i=R(s);return {receipt:i,applied:!0,changed:!Ee(r,i)}}catch(r){return Ie(e,r),{receipt:{},applied:false,changed:false}}finally{try{n?.close();}catch{}}}function U(o,t){return Y(o,()=>t,"write").applied}function L(o,t){return Y(o,t,"update")}var K={Grep:"get_project_context",Glob:"get_project_context",Agent:"get_project_context",Bash:"get_project_context",WebSearch:"get_knowledge",WebFetch:"get_knowledge"};function Ae(o){switch(o){case "WebSearch":case "WebFetch":return 1;case "Agent":return 3;case "Grep":case "Glob":return 3;case "Bash":return 5;default:return 2}}var D=class{constructor(t,e,n){this.projectRoot=t;this.sessionId=e;this.executionId=n;}getPhase(){if(!this.sessionId||!this.executionId)return "idle";let t;try{t=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250});let e=t.getContextReceipt(this.projectRoot,this.sessionId,this.executionId);if(e&&e.expiresAt>Date.now())return "context_ready";e&&t.deleteContextReceipt(this.projectRoot,this.sessionId,this.executionId);}catch{}finally{try{t?.close();}catch{}}return "context_gathering"}evaluate(t,e){let n=this.getPhase();if(e||t==="Skill")return {permissionDecision:"allow"};if(n==="context_gathering"&&K[t])return {permissionDecision:"deny",reason:`DevFlow context required. Call mcp__devflow__get_project_context, then retry ${t}.`};if(n==="context_ready")return {permissionDecision:"allow"};let r=K[t];if(!r)return {permissionDecision:"allow"};let s=L(this.projectRoot,l=>({...l,bypassCount:(l.bypassCount??0)+1}));if(!s.applied)return {permissionDecision:"allow"};let i=s.receipt.bypassCount??0,c=Ae(t);return i>=c?{permissionDecision:"allow",additionalContext:`\u5DF2 ${i} \u6B21\u76F4\u63A5\u4F7F\u7528 ${t}\uFF0C\u5EFA\u8BAE\u7528 mcp__devflow__${r} \u83B7\u53D6\u66F4\u7CBE\u786E\u7684\u4E0A\u4E0B\u6587\u3002`}:{permissionDecision:"allow"}}recordMcpCall(){U(this.projectRoot,{lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0});}};var te=1e4,Ue=3e4,C=500;function Le(o,t,e){return new Promise(n=>{try{let r=new URL(o),s=request({hostname:r.hostname,port:r.port||80,path:r.pathname+r.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":e,"Content-Length":Buffer.byteLength(t)},timeout:5e3},i=>{let c=[];i.on("data",l=>c.push(l)),i.on("end",()=>{let l=Buffer.concat(c).toString();n(i.statusCode!=null&&i.statusCode>=200&&i.statusCode<300?l:null);});});s.on("error",()=>n(null)),s.on("timeout",()=>{s.destroy(),n(null);}),s.write(t),s.end();}catch{n(null);}})}var x=class{constructor(t){this.retryScheduled=false;this.apiUrl=t?.apiUrl??process.env.DEVFLOW_API_URL??"http://127.0.0.1:13337",this.apiKey=t?.apiKey??getLocalApiKey(),this.cacheDir=t?.cacheDir??join(process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow","global"),"telemetry-cache"),this.legacyCacheDir=t?.legacyCacheDir??(t?.cacheDir?null:join(homedir(),".devflow","telemetry-cache")),this.database=t?.database??null,this.ownsDatabase=!t?.database;}async sendEvent(t){let e={...t,input:this.truncateInput(t.input)};return this.commitOrCache("tool_call",e)?(this.postHttp("/api/telemetry/tool-call",e),t.eventId):null}async sendExecutionStart(t,e,n,r,s=process.env.CLAUDE_PROJECT_DIR??process.cwd(),i=[]){let c={executionId:t,sessionId:e,skillName:n,startedAt:r,projectRoot:s,requiredMcpTools:i};this.commitOrCache("execution_start",c)&&this.postHttp("/api/telemetry/skill-execution/start",c);}async sendExecutionComplete(t,e="completed",n=Date.now(),r){let s={executionId:t,status:e,finishedAt:n,metadata:r},i=this.commitOrCache("execution_complete",s);return i&&this.postHttp("/api/telemetry/skill-execution/complete-reconciled",s),i}async sendSessionStart(t,e,n){let r={id:t,projectRoot:e,startedAt:n,label:e.split("/").pop()??"unknown"},s=this.commitOrCache("session_start",r);return s&&this.postHttp("/api/telemetry/sessions",r),s}async completeEvent(t){let e=this.commitOrCache("complete_event",t);return e&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp("/api/telemetry/tool-call/output",t)),e}async endSession(t,e=Date.now()){let n={sessionId:t,finishedAt:e},r=this.commitOrCache("session_end",n);return r&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp(`/api/telemetry/sessions/${encodeURIComponent(t)}/close`,{finishedAt:e})),r}async flushCache(){try{let n=this.getDatabase(),r=n.listTelemetryFailures({unresolvedOnly:!0,limit:C}).reverse();for(let s of r)try{this.applyOperation(s.operation,s.payload),n.resolveTelemetryFailure(s.id);}catch{}n.trimTelemetryFailures(C);}catch{}let e=[...new Set([this.cacheDir,this.legacyCacheDir].filter(n=>!!n))].filter(n=>existsSync(n)).flatMap(n=>readdirSync(n).filter(r=>r.endsWith(".json")).map(r=>{let s=join(n,r);try{return {cacheFile:s,envelope:$e(JSON.parse(readFileSync(s,"utf8")),r)}}catch{return null}})).filter(n=>n!==null).sort((n,r)=>n.envelope.timestamp-r.envelope.timestamp||ne(n.envelope.operation)-ne(r.envelope.operation));for(let{cacheFile:n,envelope:r}of e)try{let s=this.getDatabase();s.insertTelemetryFailure({id:r.id,operation:r.operation,payload:r.payload,error:r.failure,createdAt:r.timestamp}),this.applyOperation(r.operation,r.payload),s.resolveTelemetryFailure(r.id),unlinkSync(n);}catch{}this.database?.aggregatePendingToolMetrics();}aggregateMetrics(){return this.getDatabase().aggregatePendingToolMetrics()}async flushAndAggregate(){await this.flushCache(),this.aggregateMetrics();}close(){this.ownsDatabase&&this.database?.close(),this.database=null;}getDatabase(){return this.database??=openGlobalDevFlowDatabase(),this.database}commitOrCache(t,e){try{return this.applyOperation(t,e),!0}catch(n){return this.cacheOperation(t,e,n),false}}applyOperation(t,e){let n=this.getDatabase();switch(t){case "tool_call":this.ensureParentSession(n,e),n.insertToolCallEvent(e);return;case "execution_start":this.ensureParentSession(n,e),n.insertSkillExecution({executionId:e.executionId,sessionId:e.sessionId,skillName:e.skillName,startedAt:e.startedAt,status:"running"});return;case "execution_complete":n.reconcileSkillExecution(e.executionId,e.status,e.finishedAt,e.metadata);return;case "session_start":n.ensureSession(e),n.insertRun({id:re(e.id),source:"hook",tool:"session",input:{projectRoot:e.projectRoot},status:"active",startedAt:e.startedAt,tokenUsed:0,metadata:{sessionId:e.id,projectRoot:e.projectRoot}});return;case "complete_event":if(!n.updateToolCallEvent(e.eventId,{output:e.output===void 0?void 0:JSON.stringify(e.output),error:e.error,duration:e.duration}))throw new Error(`Tool call event ${e.eventId} is not available for completion`);return;case "session_end":n.closeSession(e.sessionId,e.finishedAt),n.updateRun(re(e.sessionId),{status:"completed",finishedAt:e.finishedAt});return}}ensureParentSession(t,e){let n=typeof e.sessionId=="string"?e.sessionId.trim():"";if(!n)throw new Error("Canonical session ID is required for telemetry");let r=typeof e.projectRoot=="string"&&e.projectRoot.trim()?e.projectRoot:typeof e.input?.projectRoot=="string"&&e.input.projectRoot.trim()?e.input.projectRoot:process.env.CLAUDE_PROJECT_DIR??"unknown";t.ensureSession({id:n,projectRoot:r,label:r==="unknown"?void 0:r.split("/").pop(),startedAt:Number(e.startedAt??e.timestamp??Date.now())});}cacheOperation(t,e,n){let r=Date.now(),s={id:`failure:${r}:${Math.random().toString(36).slice(2,11)}`,operation:t,payload:e,failure:n instanceof Error?n.message:String(n),timestamp:r};try{let i=this.getDatabase();i.insertTelemetryFailure({id:s.id,operation:t,payload:e,error:s.failure,createdAt:r}),i.trimTelemetryFailures(C),this.scheduleRetry();return}catch{}try{existsSync(this.cacheDir)||mkdirSync(this.cacheDir,{recursive:!0});let i=join(this.cacheDir,`${r}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync(i,JSON.stringify(s,null,2),{mode:384}),this.trimCompatibilityCache(),this.scheduleRetry();}catch{}}trimCompatibilityCache(){try{let t=readdirSync(this.cacheDir).filter(e=>e.endsWith(".json")).sort();for(let e of t.slice(0,Math.max(0,t.length-C)))unlinkSync(join(this.cacheDir,e));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},Ue).unref());}postHttp(t,e){Le(`${this.apiUrl}${t}`,JSON.stringify(e),this.apiKey);}truncateInput(t){let e=JSON.stringify(t);return e===void 0||e.length<=te?t:{_truncated:true,_original_size:e.length,_preview:`${e.substring(0,te)}...`}}};function ne(o){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(o)}function re(o){return `hook-run:${o}`}function $e(o,t){if(!o||typeof o!="object")throw new Error("Invalid telemetry cache envelope");let e=o;if(typeof e.operation=="string"&&e.payload!==void 0)return e;let n=e.type==="tool_call"?"tool_call":e.type==="execution_start"?"execution_start":null;if(!n)throw new Error("Unknown legacy telemetry cache operation");let r=e.payload??{},s=Number(r.timestamp??r.startedAt??Date.now());return {id:`legacy-cache:${t}`,operation:n,payload:r,failure:"Replayed legacy HTTP-first telemetry cache entry",timestamp:s}}function Ke(o){return Buffer.from(o,"utf8").toString("base64url")}var E=class{constructor(t){this.projectRoot=t;this.sessions=new Map;this.sessionsDir=join(g(t),"hook-sessions");}registerSession(t){let e=t.trim();if(!e)throw new Error("session_id_required");let n=this.get(e);if(n)return n.lastActivityAt=Date.now(),this.persist(n),n;let r=Date.now(),s={sessionId:e,projectRoot:this.projectRoot,requiredMcpTools:[],startedAt:r,lastActivityAt:r};return this.sessions.set(e,s),this.persist(s),s}startExecution(t,e,n){let r=this.registerSession(t);return (!r.executionId||r.skillName!==e)&&(r.executionId=`exec_${Date.now()}_${randomUUID().slice(0,8)}`),r.skillName=e,r.requiredMcpTools=[...new Set(n)],r.lastActivityAt=Date.now(),this.persist(r),r}get(t){let e=t.trim();if(!e)return null;let n=this.sessions.get(e);if(n)return n;let r=this.snapshotPath(e);if(!existsSync(r))return null;try{let s=JSON.parse(readFileSync(r,"utf8"));return s.sessionId!==e||s.projectRoot!==this.projectRoot?null:(s.requiredMcpTools=Array.isArray(s.requiredMcpTools)?s.requiredMcpTools:[],this.sessions.set(e,s),s)}catch{return null}}completeExecution(t){let e=this.get(t);if(!e)return null;let n={...e,requiredMcpTools:[...e.requiredMcpTools]};return delete e.executionId,delete e.skillName,e.requiredMcpTools=[],e.lastActivityAt=Date.now(),this.persist(e),n}removeSession(t){let e=t.trim();e&&(this.sessions.delete(e),rmSync(this.snapshotPath(e),{force:true}));}list(){return [...this.sessions.values()]}snapshotPath(t){return join(this.sessionsDir,`${Ke(t)}.json`)}persist(t){mkdirSync(this.sessionsDir,{recursive:true,mode:448});let e=this.snapshotPath(t.sessionId),n=`${e}.${process.pid}.tmp`;writeFileSync(n,JSON.stringify(t),{mode:384}),renameSync(n,e);}};var Qe=1;function et(){if(process.env.CLAUDE_PLUGIN_ROOT)return process.env.CLAUDE_PLUGIN_ROOT;try{return join(dirname(fileURLToPath(import.meta.url)),"..","..")}catch{return process.cwd()}}function ie(o=et()){let t=[join(o,"dist","command-registry.json"),join(o,"command-registry.json")];for(let e of t)try{if(!existsSync(e))continue;let n=JSON.parse(readFileSync(e,"utf8"));if(n.version!==Qe||!tt(n.commands)){console.error(`[devflow] command registry contract mismatch: ${e}`);continue}return n.commands}catch(n){console.error(`[devflow] command registry load failed: ${n.message}`);}return null}function tt(o){return !o||typeof o!="object"||Array.isArray(o)?false:Object.values(o).every(t=>{if(!t||typeof t!="object"||Array.isArray(t))return false;let e=t;return se(e.mcpTools)&&se(e.blockedNative)})}function se(o){return Array.isArray(o)&&o.every(t=>typeof t=="string"&&t.length>0)}function ae(o){let t=ie();return t?t[o]?.mcpTools??[]:[]}function ce(o){let t=o.trim().replace(/^\//,"");if(!t.startsWith("devflow:"))return null;let e=t.slice(8).replace(/^devflow-/,"");return /^[a-z0-9][a-z0-9-]*$/i.test(e)?`devflow:${e.toLowerCase()}`:null}function le(o,t){return t?`evt_tool_${createHash("sha256").update(`${o}\0${t}`).digest("hex").slice(0,24)}`:`evt_${Date.now()}_${Math.random().toString(36).slice(2,11)}`}var ut=new Set(["Agent","Bash","Glob","Grep","WebSearch","WebFetch"]);function pt(o,t,e,n){try{let r=join(g(o),"event-map.json");if(!existsSync(r))return null;let s=JSON.parse(readFileSync(r,"utf-8")),i=s.filter(a=>a.toolName===e&&(!a.sessionId||a.sessionId===t));if(i.length===0)return null;let c=n?i.find(a=>a.toolUseId===n):i.sort((a,w)=>a.timestamp-w.timestamp)[0];if(!c)return null;let l=s.findIndex(a=>a.eventId===c.eventId);return l!==-1&&s.splice(l,1),writeFileSync(r,JSON.stringify(s)),{eventId:c.eventId,timestamp:c.timestamp}}catch{return null}}function ue(o){if(!o)return 0;let t=o;if(typeof o=="string")try{t=JSON.parse(o);}catch{return 1}let e=t,n=["files","results","data","result","memories","nodes","chunks","findings","symbols","keySymbols"],r=["reasoning","riskHints","nextActions"],s=e.data??e.structuredContent??e;if(Array.isArray(s))return s.length;if(typeof s=="object"&&s!==null){let i=s,c=0;for(let a of n)Array.isArray(i[a])&&(c+=i[a].length);for(let a of r)Array.isArray(i[a])&&(c+=i[a].length);for(let[,a]of Object.entries(i))a&&typeof a=="object"&&!Array.isArray(a)&&(c+=ue(a));let l=!!(i._devflow||i._devflow_unique||i.taskType);return c===0&&l?1:c>0?c:Object.keys(i).length>0?1:0}return 0}function dt(o,t){if(!ut.has(t))return null;let e=L(o,r=>({...r,bypassCount:(r.bypassCount??0)+1}));if(!e.applied)return null;let n=e.receipt.bypassCount??0;return n>20&&n%10===0?`MCP \u5DE5\u5177\u4F7F\u7528\u7387\u4F4E\uFF1A${n} \u6B21\u76F4\u63A5\u5DE5\u5177\u8C03\u7528\u3002\u5EFA\u8BAE\u4F7F\u7528 get_project_context \u83B7\u53D6\u66F4\u7CBE\u786E\u7684\u4E0A\u4E0B\u6587\u3002`:null}async function mt(o){if(!o)return null;let t;try{t=JSON.parse(o);}catch{return null}let e=t.tool_name||"",n=M(),r=t.tool_input||{},s=t.tool_response||{},i=t.session_id?.trim()||null,c=new E(n);if(i&&c.registerSession(i),e==="Skill"){let u=typeof r=="object"&&r.skill?r.skill:"",p=ce(u);i&&p&&(c.startExecution(i,p,ae(p)),U(n,{lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0}));}e.startsWith("mcp__")&&new D(n).recordMcpCall();let l=i?c.get(i):null,a=i?pt(n,i,e,t.tool_use_id):null,w=i&&t.tool_use_id&&!a?(()=>{try{let u=openGlobalDevFlowDatabase();try{return u.getToolCallEventByToolUseId(i,t.tool_use_id)}finally{u.close();}}catch{return null}})():null,d=a??w,m=typeof s=="object"&&s!==null?s:null,f=JSON.stringify(s),h=Buffer.byteLength(f,"utf8"),b=ue(s);if(l?.executionId!==void 0||e.startsWith("mcp__devflow__")||e==="Skill"){let u=getLogWriter(n),p=l?.executionId??`auto-${i??Date.now()}`;u.toolCall({tool:e,runId:p,stepId:`step-${p}`,input:r,output:s,duration:d?Math.max(0,Date.now()-d.timestamp):0,success:!m?.error,resultCount:b,resultSizeBytes:h,error:m?.error});}if(d&&i){let u=typeof s=="string"?s:JSON.stringify(s),p=u.length>5e3?{_truncated:true,_originalSize:u.length,text:u.slice(0,5e3)}:s;await new x().completeEvent({eventId:d.eventId,sessionId:i,output:p,duration:d?Math.max(0,Date.now()-d.timestamp):0,completedAt:Date.now()});}let k=typeof r=="object"&&r.command?r.command:"",S=m?.exitCode,q=m?.stderr,pe=/^\s*(ls|cat|pwd|cd|echo|head|tail|wc|which|whoami|date|env|printenv|id|hostname|uname)\b/,y,v,I=true;if(e.startsWith("mcp__"))y="mcp_call",v={mcpTool:e.replace(/^mcp__[^_]+__/,""),query:typeof r=="object"&&r.query?r.query:null,resultCount:b,resultSizeBytes:h};else if(e==="Read"||e==="Write"||e==="Edit")y=e==="Read"?"file_read":"file_write",v={filePath:typeof r=="object"&&r.file_path?r.file_path:null,fileContentSize:h};else if(e==="Bash"){let u=pe.test(k),p=S!==void 0&&S!==0;I=!u||p,y=p?"bash_error":"bash_command",v={command:k||e,exitCode:S??null,stderr:q??null};}else e==="Agent"?(y="subagent",v={subagentType:typeof r=="object"&&r.subagent_type?r.subagent_type:null,description:typeof r=="object"&&r.description?String(r.description).slice(0,200):null}):e==="WebSearch"||e==="WebFetch"?(y=e==="WebSearch"?"web_search":"web_fetch",v={query:typeof r=="object"&&r.query?String(r.query).slice(0,200):null}):(I=false,y="tool_use",v={});if(I){let u={id:t.tool_use_id?`memory:${le(i??"",t.tool_use_id)}`:`evt:${Date.now()}:${Math.random().toString(36).slice(2,7)}`,sessionId:i??"",tool:e,kind:y,payload:v,command:k||void 0,exitCode:S,stderr:q??void 0,durationMs:d?Math.max(0,Date.now()-d.timestamp):0,createdAt:Date.now()};i&&(z(`/api/memory/session-events?rootPath=${encodeURIComponent(n)}`,u),(async()=>{let p=new MemoryGate(n);try{await p.forceWarmUp(),await p.recordEvent(u);}catch{}finally{p.close();}})());}let T=dt(n,e);return T&&console.error("[devflow] Enforcement:",T),T}if(process.argv[1]?.endsWith("post-tool-use")||process.argv[1]?.endsWith("post-tool-use.js")){let o="";process.stdin.setEncoding("utf8"),process.stdin.on("data",t=>{o+=t;}),process.stdin.on("end",async()=>{let t=await mt(o.trim()||process.argv[2]||"");if(t){let e=JSON.stringify(t);console.log(JSON.stringify({hookSpecificOutput:{hookEventName:"PostToolUse",additionalContext:e}}));}}),process.stdin.on("error",()=>process.exit(0)),setTimeout(()=>process.exit(0),5e3).unref();}
|
|
3
|
-
export{
|
|
1
|
+
import {existsSync,readFileSync,rmSync,mkdirSync,writeFileSync,renameSync,readdirSync,unlinkSync,appendFileSync}from'fs';import {join,dirname}from'path';import {getLogWriter}from'@devflow-tools/telemetry';import {request}from'http';import {homedir}from'os';import {getLocalApiKey,getProjectStateDir}from'@devflow-tools/sdk';import {openGlobalDevFlowDatabase}from'@devflow-tools/database';import {randomUUID,createHash}from'crypto';import {fileURLToPath}from'url';function H(o,t,e){try{let n=openGlobalDevFlowDatabase();try{n.insertEvent({kind:o,timestamp:Date.now(),duration:e,success:t.success!==!1,metadata:t});}finally{n.close();}}catch{}}var F=process.env.DEVFLOW_SERVER_URL||"http://127.0.0.1:13337",we=getLocalApiKey(),_e=8,be=6e4,W=new Map;function Se(o){let t=new URL(o,F).pathname,e=W.get(t);return e||(e={failures:0,lastFailure:0,openUntil:0,status:"closed"},W.set(t,e)),e}function M(o,t,e){if(t.status===e)return;let n=t.status;t.status=e,H("http_circuit_transition",{path:new URL(o,F).pathname,previous:n,status:e,failures:t.failures,openUntil:t.openUntil});}function A(o,t){t.failures++,t.lastFailure=Date.now(),t.failures>=_e&&(t.openUntil=Date.now()+be,M(o,t,"open"));}function Re(o,t){let e=t.status!=="closed";t.failures=0,t.openUntil=0,e&&M(o,t,"closed");}var O=join(homedir(),".devflow","errors");function J(o,t){try{existsSync(O)||mkdirSync(O,{recursive:!0});let e=`${new Date().toISOString()} | ${o} | ${t?.message??String(t)}
|
|
2
|
+
`;appendFileSync(join(O,"http-errors.log"),e);}catch{}}function K(o,t,e={}){let n=e.timeout??5e3,r=e.maxRetries??2,s=e.circuitBreaker??true,i=Se(o);if(s&&i.openUntil>Date.now())return Promise.resolve();s&&i.status==="open"&&M(o,i,"half_open");let a=c=>new Promise(l=>{let u=JSON.stringify(t),d=new URL(o,F),f=request({hostname:d.hostname,port:d.port,path:d.pathname+d.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":we,"Content-Length":Buffer.byteLength(u)},timeout:n},g=>{if(g.resume(),g.statusCode&&g.statusCode>=400){let y=new Error(`HTTP ${g.statusCode}`);if(J(o,y),c>0){let b=Math.min(1e3*Math.pow(2,r-c),8e3);setTimeout(()=>{a(c-1).then(l);},b);}else A(o,i),l();return}Re(o,i),l();});f.on("error",g=>{if(J(o,g),c>0){let y=Math.min(1e3*Math.pow(2,r-c),8e3);setTimeout(()=>{a(c-1).then(l);},y);}else A(o,i),l();}),f.on("timeout",()=>{f.destroy(),c>0?a(c-1).then(l):(A(o,i),l());}),f.write(u),f.end();});return a(r)}function j(o){return o??process.env.CLAUDE_PROJECT_DIR??process.cwd()}function h(o){return process.env.DEVFLOW_STATE_DIR??getProjectStateDir(j(o))}function R(o){if(!o||typeof o!="object"||Array.isArray(o))return {};let t=o,e={};for(let n of ["lastMcpCall","bypassCount"])if(n in t){let r=t[n];if(r==null)continue;e[n]=typeof r=="number"&&Number.isFinite(r)&&r>=0?r:0;}return e}function Ee(o,t){return o.lastMcpCall===t.lastMcpCall&&o.bypassCount===t.bypassCount}function ke(o){try{return existsSync(o)?R(JSON.parse(readFileSync(o,"utf-8"))):{}}catch{return {}}}function Ie(o,t){let e=t instanceof Error?t.message:String(t);console.error(`[devflow] Receipt ${o} skipped: ${e}`);}function z(o){let t=h(o);for(let e of ["receipt.json","receipt-lock.sqlite","receipt-lock.sqlite-shm","receipt-lock.sqlite-wal"])try{existsSync(join(t,e))&&unlinkSync(join(t,e));}catch{}}function Te(o,t){if(o.getHookReceipt(t)){z(t);return}let e=join(h(t),"receipt.json");if(!existsSync(e))return;let n=ke(e);o.updateHookReceipt(t,()=>n),z(t);}function Y(o,t,e){let n;try{n=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250}),Te(n,o);let r={},s=n.updateHookReceipt(o,a=>(r=R(a),R(t(r)))),i=R(s);return {receipt:i,applied:!0,changed:!Ee(r,i)}}catch(r){return Ie(e,r),{receipt:{},applied:false,changed:false}}finally{try{n?.close();}catch{}}}function U(o,t){return Y(o,()=>t,"write").applied}function L(o,t){return Y(o,t,"update")}var B={Grep:"get_project_context",Glob:"get_project_context",Agent:"get_project_context",Bash:"get_project_context",WebSearch:"get_knowledge",WebFetch:"get_knowledge"};function Ae(o){switch(o){case "WebSearch":case "WebFetch":return 1;case "Agent":return 3;case "Grep":case "Glob":return 3;case "Bash":return 5;default:return 2}}var D=class{constructor(t,e,n){this.projectRoot=t;this.sessionId=e;this.executionId=n;}getPhase(){if(!this.sessionId||!this.executionId)return "idle";let t;try{t=openGlobalDevFlowDatabase(void 0,{busyTimeoutMs:250});let e=t.getContextReceipt(this.projectRoot,this.sessionId,this.executionId);if(e&&e.expiresAt>Date.now())return "context_ready";e&&t.deleteContextReceipt(this.projectRoot,this.sessionId,this.executionId);}catch{}finally{try{t?.close();}catch{}}return "context_gathering"}evaluate(t,e){let n=this.getPhase();if(e||t==="Skill")return {permissionDecision:"allow"};if(n==="context_gathering"&&B[t])return {permissionDecision:"deny",reason:`DevFlow context required. Call mcp__devflow__get_project_context, then retry ${t}.`};if(n==="context_ready")return {permissionDecision:"allow"};let r=B[t];if(!r)return {permissionDecision:"allow"};let s=L(this.projectRoot,c=>({...c,bypassCount:(c.bypassCount??0)+1}));if(!s.applied)return {permissionDecision:"allow"};let i=s.receipt.bypassCount??0,a=Ae(t);return i>=a?{permissionDecision:"allow",additionalContext:`\u5DF2 ${i} \u6B21\u76F4\u63A5\u4F7F\u7528 ${t}\uFF0C\u5EFA\u8BAE\u7528 mcp__devflow__${r} \u83B7\u53D6\u66F4\u7CBE\u786E\u7684\u4E0A\u4E0B\u6587\u3002`}:{permissionDecision:"allow"}}recordMcpCall(){U(this.projectRoot,{lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0});}};var te=1e4,Ue=3e4,C=500;function Le(o,t,e){return new Promise(n=>{try{let r=new URL(o),s=request({hostname:r.hostname,port:r.port||80,path:r.pathname+r.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":e,"Content-Length":Buffer.byteLength(t)},timeout:5e3},i=>{let a=[];i.on("data",c=>a.push(c)),i.on("end",()=>{let c=Buffer.concat(a).toString();n(i.statusCode!=null&&i.statusCode>=200&&i.statusCode<300?c:null);});});s.on("error",()=>n(null)),s.on("timeout",()=>{s.destroy(),n(null);}),s.write(t),s.end();}catch{n(null);}})}var x=class{constructor(t){this.retryScheduled=false;this.metricAggregationScheduled=false;this.apiUrl=t?.apiUrl??process.env.DEVFLOW_API_URL??"http://127.0.0.1:13337",this.apiKey=t?.apiKey??getLocalApiKey(),this.cacheDir=t?.cacheDir??join(process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow","global"),"telemetry-cache"),this.legacyCacheDir=t?.legacyCacheDir??(t?.cacheDir?null:join(homedir(),".devflow","telemetry-cache")),this.database=t?.database??null,this.ownsDatabase=!t?.database;}async sendEvent(t){let e={...t,input:this.truncateInput(t.input)};return this.commitOrCache("tool_call",e)?(this.postHttp("/api/telemetry/tool-call",e),t.eventId):null}async sendExecutionStart(t,e,n,r,s=process.env.CLAUDE_PROJECT_DIR??process.cwd(),i=[]){let a={executionId:t,sessionId:e,skillName:n,startedAt:r,projectRoot:s,requiredMcpTools:i};this.commitOrCache("execution_start",a)&&this.postHttp("/api/telemetry/skill-execution/start",a);}async sendExecutionComplete(t,e="completed",n=Date.now(),r){let s={executionId:t,status:e,finishedAt:n,metadata:r},i=this.commitOrCache("execution_complete",s);return i&&this.postHttp("/api/telemetry/skill-execution/complete-reconciled",s),i}async sendSessionStart(t,e,n){let r={id:t,projectRoot:e,startedAt:n,label:e.split("/").pop()??"unknown"},s=this.commitOrCache("session_start",r);return s&&this.postHttp("/api/telemetry/sessions",r),s}async completeEvent(t){let e=this.commitOrCache("complete_event",t);return e&&(this.postHttp("/api/telemetry/tool-call/output",t),this.scheduleMetricAggregation()),e}async endSession(t,e=Date.now()){let n={sessionId:t,finishedAt:e},r=this.commitOrCache("session_end",n);return r&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp(`/api/telemetry/sessions/${encodeURIComponent(t)}/close`,{finishedAt:e})),r}async flushCache(){try{let n=this.getDatabase(),r=n.listTelemetryFailures({unresolvedOnly:!0,limit:C}).reverse();for(let s of r)try{this.applyOperation(s.operation,s.payload),n.resolveTelemetryFailure(s.id);}catch{}n.trimTelemetryFailures(C);}catch{}let e=[...new Set([this.cacheDir,this.legacyCacheDir].filter(n=>!!n))].filter(n=>existsSync(n)).flatMap(n=>readdirSync(n).filter(r=>r.endsWith(".json")).map(r=>{let s=join(n,r);try{return {cacheFile:s,envelope:$e(JSON.parse(readFileSync(s,"utf8")),r)}}catch{return null}})).filter(n=>n!==null).sort((n,r)=>n.envelope.timestamp-r.envelope.timestamp||ne(n.envelope.operation)-ne(r.envelope.operation));for(let{cacheFile:n,envelope:r}of e)try{let s=this.getDatabase();s.insertTelemetryFailure({id:r.id,operation:r.operation,payload:r.payload,error:r.failure,createdAt:r.timestamp}),this.applyOperation(r.operation,r.payload),s.resolveTelemetryFailure(r.id),unlinkSync(n);}catch{}this.database?.aggregatePendingToolMetrics();}aggregateMetrics(){return this.getDatabase().aggregatePendingToolMetrics()}async flushAndAggregate(){await this.flushCache(),this.aggregateMetrics();}close(){this.ownsDatabase&&this.database?.close(),this.database=null;}getDatabase(){return this.database??=openGlobalDevFlowDatabase(),this.database}commitOrCache(t,e){try{return this.applyOperation(t,e),!0}catch(n){return this.cacheOperation(t,e,n),false}}applyOperation(t,e){let n=this.getDatabase();switch(t){case "tool_call":this.ensureParentSession(n,e),n.insertToolCallEvent(e);return;case "execution_start":this.ensureParentSession(n,e),n.insertSkillExecution({executionId:e.executionId,sessionId:e.sessionId,skillName:e.skillName,startedAt:e.startedAt,status:"running",requiredMcpTools:e.requiredMcpTools});return;case "execution_complete":n.reconcileSkillExecution(e.executionId,e.status,e.finishedAt,e.metadata);return;case "session_start":n.ensureSession(e),n.insertRun({id:re(e.id),source:"hook",tool:"session",input:{projectRoot:e.projectRoot},status:"active",startedAt:e.startedAt,tokenUsed:0,metadata:{sessionId:e.id,projectRoot:e.projectRoot}});return;case "complete_event":if(!n.updateToolCallEvent(e.eventId,{output:e.output===void 0?void 0:JSON.stringify(e.output),error:e.error,duration:e.duration}))throw new Error(`Tool call event ${e.eventId} is not available for completion`);return;case "session_end":n.closeSession(e.sessionId,e.finishedAt),n.updateRun(re(e.sessionId),{status:"completed",finishedAt:e.finishedAt});return}}ensureParentSession(t,e){let n=typeof e.sessionId=="string"?e.sessionId.trim():"";if(!n)throw new Error("Canonical session ID is required for telemetry");let r=typeof e.projectRoot=="string"&&e.projectRoot.trim()?e.projectRoot:typeof e.input?.projectRoot=="string"&&e.input.projectRoot.trim()?e.input.projectRoot:process.env.CLAUDE_PROJECT_DIR??"unknown";t.ensureSession({id:n,projectRoot:r,label:r==="unknown"?void 0:r.split("/").pop(),startedAt:Number(e.startedAt??e.timestamp??Date.now())});}cacheOperation(t,e,n){let r=Date.now(),s={id:`failure:${r}:${Math.random().toString(36).slice(2,11)}`,operation:t,payload:e,failure:n instanceof Error?n.message:String(n),timestamp:r};try{let i=this.getDatabase();i.insertTelemetryFailure({id:s.id,operation:t,payload:e,error:s.failure,createdAt:r}),i.trimTelemetryFailures(C),this.scheduleRetry();return}catch{}try{existsSync(this.cacheDir)||mkdirSync(this.cacheDir,{recursive:!0});let i=join(this.cacheDir,`${r}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync(i,JSON.stringify(s,null,2),{mode:384}),this.trimCompatibilityCache(),this.scheduleRetry();}catch{}}trimCompatibilityCache(){try{let t=readdirSync(this.cacheDir).filter(e=>e.endsWith(".json")).sort();for(let e of t.slice(0,Math.max(0,t.length-C)))unlinkSync(join(this.cacheDir,e));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},Ue).unref());}scheduleMetricAggregation(){if(this.metricAggregationScheduled||process.env.DEVFLOW_HOOK_DEGRADED==="1")return;this.metricAggregationScheduled=true,setTimeout(()=>{this.metricAggregationScheduled=false;try{this.getDatabase().aggregatePendingToolMetrics(50)===50&&this.scheduleMetricAggregation();}catch{}},25).unref();}postHttp(t,e){Le(`${this.apiUrl}${t}`,JSON.stringify(e),this.apiKey);}truncateInput(t){let e=JSON.stringify(t);return e===void 0||e.length<=te?t:{_truncated:true,_original_size:e.length,_preview:`${e.substring(0,te)}...`}}};function ne(o){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(o)}function re(o){return `hook-run:${o}`}function $e(o,t){if(!o||typeof o!="object")throw new Error("Invalid telemetry cache envelope");let e=o;if(typeof e.operation=="string"&&e.payload!==void 0)return e;let n=e.type==="tool_call"?"tool_call":e.type==="execution_start"?"execution_start":null;if(!n)throw new Error("Unknown legacy telemetry cache operation");let r=e.payload??{},s=Number(r.timestamp??r.startedAt??Date.now());return {id:`legacy-cache:${t}`,operation:n,payload:r,failure:"Replayed legacy HTTP-first telemetry cache entry",timestamp:s}}function Be(o){return Buffer.from(o,"utf8").toString("base64url")}var E=class{constructor(t){this.projectRoot=t;this.sessions=new Map;this.sessionsDir=join(h(t),"hook-sessions");}registerSession(t){let e=t.trim();if(!e)throw new Error("session_id_required");let n=this.get(e);if(n)return n.lastActivityAt=Date.now(),this.persist(n),n;let r=Date.now(),s={sessionId:e,projectRoot:this.projectRoot,requiredMcpTools:[],startedAt:r,lastActivityAt:r};return this.sessions.set(e,s),this.persist(s),s}startExecution(t,e,n){let r=this.registerSession(t);return (!r.executionId||r.skillName!==e)&&(r.executionId=`exec_${Date.now()}_${randomUUID().slice(0,8)}`),r.skillName=e,r.requiredMcpTools=[...new Set(n)],r.lastActivityAt=Date.now(),this.persist(r),r}get(t){let e=t.trim();if(!e)return null;let n=this.sessions.get(e);if(n)return n;let r=this.snapshotPath(e);if(!existsSync(r))return null;try{let s=JSON.parse(readFileSync(r,"utf8"));return s.sessionId!==e||s.projectRoot!==this.projectRoot?null:(s.requiredMcpTools=Array.isArray(s.requiredMcpTools)?s.requiredMcpTools:[],this.sessions.set(e,s),s)}catch{return null}}completeExecution(t){let e=this.get(t);if(!e)return null;let n={...e,requiredMcpTools:[...e.requiredMcpTools]};return delete e.executionId,delete e.skillName,e.requiredMcpTools=[],e.lastActivityAt=Date.now(),this.persist(e),n}removeSession(t){let e=t.trim();e&&(this.sessions.delete(e),rmSync(this.snapshotPath(e),{force:true}));}list(){return [...this.sessions.values()]}snapshotPath(t){return join(this.sessionsDir,`${Be(t)}.json`)}persist(t){mkdirSync(this.sessionsDir,{recursive:true,mode:448});let e=this.snapshotPath(t.sessionId),n=`${e}.${process.pid}.tmp`;writeFileSync(n,JSON.stringify(t),{mode:384}),renameSync(n,e);}};var Qe=1;function et(){if(process.env.CLAUDE_PLUGIN_ROOT)return process.env.CLAUDE_PLUGIN_ROOT;try{return join(dirname(fileURLToPath(import.meta.url)),"..","..")}catch{return process.cwd()}}function ie(o=et()){let t=[join(o,"dist","command-registry.json"),join(o,"command-registry.json")];for(let e of t)try{if(!existsSync(e))continue;let n=JSON.parse(readFileSync(e,"utf8"));if(n.version!==Qe||!tt(n.commands)){console.error(`[devflow] command registry contract mismatch: ${e}`);continue}return n.commands}catch(n){console.error(`[devflow] command registry load failed: ${n.message}`);}return null}function tt(o){return !o||typeof o!="object"||Array.isArray(o)?false:Object.values(o).every(t=>{if(!t||typeof t!="object"||Array.isArray(t))return false;let e=t;return se(e.mcpTools)&&se(e.blockedNative)})}function se(o){return Array.isArray(o)&&o.every(t=>typeof t=="string"&&t.length>0)}function ae(o){let t=ie();return t?t[o]?.mcpTools??[]:[]}function ce(o){let t=o.trim().replace(/^\//,"");if(!t.startsWith("devflow:"))return null;let e=t.slice(8).replace(/^devflow-/,"");return /^[a-z0-9][a-z0-9-]*$/i.test(e)?`devflow:${e.toLowerCase()}`:null}function le(o,t){return t?`evt_tool_${createHash("sha256").update(`${o}\0${t}`).digest("hex").slice(0,24)}`:`evt_${Date.now()}_${Math.random().toString(36).slice(2,11)}`}var lt=new Set(["Agent","Bash","Glob","Grep","WebSearch","WebFetch"]);function ut(o,t,e,n){try{let r=join(h(o),"event-map.json");if(!existsSync(r))return null;let s=JSON.parse(readFileSync(r,"utf-8")),i=s.filter(l=>l.toolName===e&&(!l.sessionId||l.sessionId===t));if(i.length===0)return null;let a=n?i.find(l=>l.toolUseId===n):i.sort((l,u)=>l.timestamp-u.timestamp)[0];if(!a)return null;let c=s.findIndex(l=>l.eventId===a.eventId);return c!==-1&&s.splice(c,1),writeFileSync(r,JSON.stringify(s)),{eventId:a.eventId,timestamp:a.timestamp}}catch{return null}}function ue(o){if(!o)return 0;let t=o;if(typeof o=="string")try{t=JSON.parse(o);}catch{return 1}let e=t,n=["files","results","data","result","memories","nodes","chunks","findings","symbols","keySymbols"],r=["reasoning","riskHints","nextActions"],s=Array.isArray(e.data)?e.data:e.structuredContent??e;if(Array.isArray(s))return s.length;if(typeof s=="object"&&s!==null){let i=s,a=0,c=false;for(let u of n)Array.isArray(i[u])&&(c=true,a+=i[u].length);for(let u of r)Array.isArray(i[u])&&(a+=i[u].length);for(let[,u]of Object.entries(i))u&&typeof u=="object"&&!Array.isArray(u)&&(a+=ue(u));let l=!!(i._devflow||i._devflow_unique||i.taskType);return a===0&&l&&!c?1:a>0?a:c?0:Object.keys(i).length>0?1:0}return 0}function pt(o,t){if(!lt.has(t))return null;let e=L(o,r=>({...r,bypassCount:(r.bypassCount??0)+1}));if(!e.applied)return null;let n=e.receipt.bypassCount??0;return n>20&&n%10===0?`MCP \u5DE5\u5177\u4F7F\u7528\u7387\u4F4E\uFF1A${n} \u6B21\u76F4\u63A5\u5DE5\u5177\u8C03\u7528\u3002\u5EFA\u8BAE\u4F7F\u7528 get_project_context \u83B7\u53D6\u66F4\u7CBE\u786E\u7684\u4E0A\u4E0B\u6587\u3002`:null}async function dt(o){if(!o)return null;let t;try{t=JSON.parse(o);}catch{return null}let e=t.tool_name||"",n=j(),r=t.tool_input||{},s=t.tool_response||{},i=t.session_id?.trim()||null,a=new E(n);if(i&&a.registerSession(i),e==="Skill"){let p=typeof r=="object"&&r.skill?r.skill:"",m=ce(p);i&&m&&(a.startExecution(i,m,ae(m)),U(n,{lastMcpCall:Math.floor(Date.now()/1e3),bypassCount:0}));}e.startsWith("mcp__")&&new D(n).recordMcpCall();let c=i?a.get(i):null,l=i?ut(n,i,e,t.tool_use_id):null,u=i&&t.tool_use_id&&!l?(()=>{try{let p=openGlobalDevFlowDatabase();try{return p.getToolCallEventByToolUseId(i,t.tool_use_id)}finally{p.close();}}catch{return null}})():null,d=l??u,f=typeof s=="object"&&s!==null?s:null,g=JSON.stringify(s),y=Buffer.byteLength(g,"utf8"),b=ue(s);if(c?.executionId!==void 0||e.startsWith("mcp__devflow__")||e==="Skill"){let p=getLogWriter(n),m=c?.executionId??`auto-${i??Date.now()}`;p.toolCall({tool:e,runId:m,stepId:`step-${m}`,input:r,output:s,duration:d?Math.max(0,Date.now()-d.timestamp):0,success:!f?.error,resultCount:b,resultSizeBytes:y,error:f?.error});}if(d&&i){let p=typeof s=="string"?s:JSON.stringify(s),m=p.length>5e3?{_truncated:true,_originalSize:p.length,text:p.slice(0,5e3)}:s;await new x().completeEvent({eventId:d.eventId,sessionId:i,output:m,duration:d?Math.max(0,Date.now()-d.timestamp):0,completedAt:Date.now()});}let k=typeof r=="object"&&r.command?r.command:"",S=f?.exitCode,q=f?.stderr,pe=/^\s*(ls|cat|pwd|cd|echo|head|tail|wc|which|whoami|date|env|printenv|id|hostname|uname)\b/,v,w,I=true;if(e.startsWith("mcp__"))v="mcp_call",w={mcpTool:e.replace(/^mcp__[^_]+__/,""),query:typeof r=="object"&&r.query?r.query:null,resultCount:b,resultSizeBytes:y};else if(e==="Read"||e==="Write"||e==="Edit")v=e==="Read"?"file_read":"file_write",w={filePath:typeof r=="object"&&r.file_path?r.file_path:null,fileContentSize:y};else if(e==="Bash"){let p=pe.test(k),m=S!==void 0&&S!==0;I=!p||m,v=m?"bash_error":"bash_command",w={command:k||e,exitCode:S??null,stderr:q??null};}else e==="Agent"?(v="subagent",w={subagentType:typeof r=="object"&&r.subagent_type?r.subagent_type:null,description:typeof r=="object"&&r.description?String(r.description).slice(0,200):null}):e==="WebSearch"||e==="WebFetch"?(v=e==="WebSearch"?"web_search":"web_fetch",w={query:typeof r=="object"&&r.query?String(r.query).slice(0,200):null}):(I=false,v="tool_use",w={});if(I){let p={id:t.tool_use_id?`memory:${le(i??"",t.tool_use_id)}`:`evt:${Date.now()}:${Math.random().toString(36).slice(2,7)}`,sessionId:i??"",tool:e,kind:v,payload:w,command:k||void 0,exitCode:S,stderr:q??void 0,durationMs:d?Math.max(0,Date.now()-d.timestamp):0,createdAt:Date.now()};i&&K(`/api/memory/session-events?rootPath=${encodeURIComponent(n)}`,p);}let T=pt(n,e);return T&&console.error("[devflow] Enforcement:",T),T}if(process.argv[1]?.endsWith("post-tool-use")||process.argv[1]?.endsWith("post-tool-use.js")){let o="";process.stdin.setEncoding("utf8"),process.stdin.on("data",t=>{o+=t;}),process.stdin.on("end",async()=>{let t=await dt(o.trim()||process.argv[2]||"");if(t){let e=JSON.stringify(t);console.log(JSON.stringify({hookSpecificOutput:{hookEventName:"PostToolUse",additionalContext:e}}));}}),process.stdin.on("error",()=>process.exit(0)),setTimeout(()=>process.exit(0),5e3).unref();}
|
|
3
|
+
export{dt as handlePostToolUse};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {getLocalApiKey}from'@devflow-tools/sdk';import {existsSync,readdirSync,readFileSync,unlinkSync,mkdirSync,writeFileSync}from'fs';import {request}from'http';import {join}from'path';import {homedir}from'os';import {openGlobalDevFlowDatabase}from'@devflow-tools/database';import {MemoryGate}from'@devflow-tools/memory-engine';function
|
|
1
|
+
import {getLocalApiKey}from'@devflow-tools/sdk';import {existsSync,readdirSync,readFileSync,unlinkSync,mkdirSync,writeFileSync}from'fs';import {request}from'http';import {join}from'path';import {homedir}from'os';import {openGlobalDevFlowDatabase}from'@devflow-tools/database';import {MemoryGate}from'@devflow-tools/memory-engine';function d(s){return s??process.env.CLAUDE_PROJECT_DIR??process.cwd()}var y=1e4,S=3e4,u=500;function A(s,r,e){return new Promise(t=>{try{let o=new URL(s),n=request({hostname:o.hostname,port:o.port||80,path:o.pathname+o.search,method:"POST",headers:{"Content-Type":"application/json","X-API-Key":e,"Content-Length":Buffer.byteLength(r)},timeout:5e3},i=>{let a=[];i.on("data",l=>a.push(l)),i.on("end",()=>{let l=Buffer.concat(a).toString();t(i.statusCode!=null&&i.statusCode>=200&&i.statusCode<300?l:null);});});n.on("error",()=>t(null)),n.on("timeout",()=>{n.destroy(),t(null);}),n.write(r),n.end();}catch{t(null);}})}var m=class{constructor(r){this.retryScheduled=false;this.metricAggregationScheduled=false;this.apiUrl=r?.apiUrl??process.env.DEVFLOW_API_URL??"http://127.0.0.1:13337",this.apiKey=r?.apiKey??getLocalApiKey(),this.cacheDir=r?.cacheDir??join(process.env.DEVFLOW_STATE_DIR??join(homedir(),".devflow","global"),"telemetry-cache"),this.legacyCacheDir=r?.legacyCacheDir??(r?.cacheDir?null:join(homedir(),".devflow","telemetry-cache")),this.database=r?.database??null,this.ownsDatabase=!r?.database;}async sendEvent(r){let e={...r,input:this.truncateInput(r.input)};return this.commitOrCache("tool_call",e)?(this.postHttp("/api/telemetry/tool-call",e),r.eventId):null}async sendExecutionStart(r,e,t,o,n=process.env.CLAUDE_PROJECT_DIR??process.cwd(),i=[]){let a={executionId:r,sessionId:e,skillName:t,startedAt:o,projectRoot:n,requiredMcpTools:i};this.commitOrCache("execution_start",a)&&this.postHttp("/api/telemetry/skill-execution/start",a);}async sendExecutionComplete(r,e="completed",t=Date.now(),o){let n={executionId:r,status:e,finishedAt:t,metadata:o},i=this.commitOrCache("execution_complete",n);return i&&this.postHttp("/api/telemetry/skill-execution/complete-reconciled",n),i}async sendSessionStart(r,e,t){let o={id:r,projectRoot:e,startedAt:t,label:e.split("/").pop()??"unknown"},n=this.commitOrCache("session_start",o);return n&&this.postHttp("/api/telemetry/sessions",o),n}async completeEvent(r){let e=this.commitOrCache("complete_event",r);return e&&(this.postHttp("/api/telemetry/tool-call/output",r),this.scheduleMetricAggregation()),e}async endSession(r,e=Date.now()){let t={sessionId:r,finishedAt:e},o=this.commitOrCache("session_end",t);return o&&(this.getDatabase().aggregatePendingToolMetrics(),this.postHttp(`/api/telemetry/sessions/${encodeURIComponent(r)}/close`,{finishedAt:e})),o}async flushCache(){try{let t=this.getDatabase(),o=t.listTelemetryFailures({unresolvedOnly:!0,limit:u}).reverse();for(let n of o)try{this.applyOperation(n.operation,n.payload),t.resolveTelemetryFailure(n.id);}catch{}t.trimTelemetryFailures(u);}catch{}let e=[...new Set([this.cacheDir,this.legacyCacheDir].filter(t=>!!t))].filter(t=>existsSync(t)).flatMap(t=>readdirSync(t).filter(o=>o.endsWith(".json")).map(o=>{let n=join(t,o);try{return {cacheFile:n,envelope:x(JSON.parse(readFileSync(n,"utf8")),o)}}catch{return null}})).filter(t=>t!==null).sort((t,o)=>t.envelope.timestamp-o.envelope.timestamp||v(t.envelope.operation)-v(o.envelope.operation));for(let{cacheFile:t,envelope:o}of e)try{let n=this.getDatabase();n.insertTelemetryFailure({id:o.id,operation:o.operation,payload:o.payload,error:o.failure,createdAt:o.timestamp}),this.applyOperation(o.operation,o.payload),n.resolveTelemetryFailure(o.id),unlinkSync(t);}catch{}this.database?.aggregatePendingToolMetrics();}aggregateMetrics(){return this.getDatabase().aggregatePendingToolMetrics()}async flushAndAggregate(){await this.flushCache(),this.aggregateMetrics();}close(){this.ownsDatabase&&this.database?.close(),this.database=null;}getDatabase(){return this.database??=openGlobalDevFlowDatabase(),this.database}commitOrCache(r,e){try{return this.applyOperation(r,e),!0}catch(t){return this.cacheOperation(r,e,t),false}}applyOperation(r,e){let t=this.getDatabase();switch(r){case "tool_call":this.ensureParentSession(t,e),t.insertToolCallEvent(e);return;case "execution_start":this.ensureParentSession(t,e),t.insertSkillExecution({executionId:e.executionId,sessionId:e.sessionId,skillName:e.skillName,startedAt:e.startedAt,status:"running",requiredMcpTools:e.requiredMcpTools});return;case "execution_complete":t.reconcileSkillExecution(e.executionId,e.status,e.finishedAt,e.metadata);return;case "session_start":t.ensureSession(e),t.insertRun({id:D(e.id),source:"hook",tool:"session",input:{projectRoot:e.projectRoot},status:"active",startedAt:e.startedAt,tokenUsed:0,metadata:{sessionId:e.id,projectRoot:e.projectRoot}});return;case "complete_event":if(!t.updateToolCallEvent(e.eventId,{output:e.output===void 0?void 0:JSON.stringify(e.output),error:e.error,duration:e.duration}))throw new Error(`Tool call event ${e.eventId} is not available for completion`);return;case "session_end":t.closeSession(e.sessionId,e.finishedAt),t.updateRun(D(e.sessionId),{status:"completed",finishedAt:e.finishedAt});return}}ensureParentSession(r,e){let t=typeof e.sessionId=="string"?e.sessionId.trim():"";if(!t)throw new Error("Canonical session ID is required for telemetry");let o=typeof e.projectRoot=="string"&&e.projectRoot.trim()?e.projectRoot:typeof e.input?.projectRoot=="string"&&e.input.projectRoot.trim()?e.input.projectRoot:process.env.CLAUDE_PROJECT_DIR??"unknown";r.ensureSession({id:t,projectRoot:o,label:o==="unknown"?void 0:o.split("/").pop(),startedAt:Number(e.startedAt??e.timestamp??Date.now())});}cacheOperation(r,e,t){let o=Date.now(),n={id:`failure:${o}:${Math.random().toString(36).slice(2,11)}`,operation:r,payload:e,failure:t instanceof Error?t.message:String(t),timestamp:o};try{let i=this.getDatabase();i.insertTelemetryFailure({id:n.id,operation:r,payload:e,error:n.failure,createdAt:o}),i.trimTelemetryFailures(u),this.scheduleRetry();return}catch{}try{existsSync(this.cacheDir)||mkdirSync(this.cacheDir,{recursive:!0});let i=join(this.cacheDir,`${o}_${Math.random().toString(36).slice(2,11)}.json`);writeFileSync(i,JSON.stringify(n,null,2),{mode:384}),this.trimCompatibilityCache(),this.scheduleRetry();}catch{}}trimCompatibilityCache(){try{let r=readdirSync(this.cacheDir).filter(e=>e.endsWith(".json")).sort();for(let e of r.slice(0,Math.max(0,r.length-u)))unlinkSync(join(this.cacheDir,e));}catch{}}scheduleRetry(){this.retryScheduled||(this.retryScheduled=true,setTimeout(()=>{this.retryScheduled=false,this.flushCache();},S).unref());}scheduleMetricAggregation(){if(this.metricAggregationScheduled||process.env.DEVFLOW_HOOK_DEGRADED==="1")return;this.metricAggregationScheduled=true,setTimeout(()=>{this.metricAggregationScheduled=false;try{this.getDatabase().aggregatePendingToolMetrics(50)===50&&this.scheduleMetricAggregation();}catch{}},25).unref();}postHttp(r,e){A(`${this.apiUrl}${r}`,JSON.stringify(e),this.apiKey);}truncateInput(r){let e=JSON.stringify(r);return e===void 0||e.length<=y?r:{_truncated:true,_original_size:e.length,_preview:`${e.substring(0,y)}...`}}};function v(s){return ["session_start","execution_start","tool_call","complete_event","execution_complete","session_end"].indexOf(s)}function D(s){return `hook-run:${s}`}function x(s,r){if(!s||typeof s!="object")throw new Error("Invalid telemetry cache envelope");let e=s;if(typeof e.operation=="string"&&e.payload!==void 0)return e;let t=e.type==="tool_call"?"tool_call":e.type==="execution_start"?"execution_start":null;if(!t)throw new Error("Unknown legacy telemetry cache operation");let o=e.payload??{},n=Number(o.timestamp??o.startedAt??Date.now());return {id:`legacy-cache:${r}`,operation:t,payload:o,failure:"Replayed legacy HTTP-first telemetry cache entry",timestamp:n}}async function _(s){let r=!s.memory,e=s.memory??new MemoryGate(s.projectRoot),t=0,o=0;try{await e.forceWarmUp(),s.trigger==="session_end"&&(t=await e.releasePendingDistillLeases(s.sessionId)),o=e.getPendingEventCount();}finally{r&&e.close();}let n={pendingEvents:o,releasedLeases:t,trigger:s.trigger,sessionId:s.sessionId};try{let i=openGlobalDevFlowDatabase();try{let a=Date.now(),l=`distill:${a}:${Math.random().toString(36).slice(2,10)}`;i.recordMemoryDistillCheckpoint({id:l,projectRoot:s.projectRoot,sessionId:s.sessionId,trigger:s.trigger,pendingEvents:o,releasedLeases:t,createdAt:a}),i.insertEvent({kind:"memory_distill_requested",timestamp:a,success:!0,metadata:{...n,projectRoot:s.projectRoot}});}finally{i.close();}}catch{}return n}function b(s){if(s.pendingEvents===0)return "";let r=s.sessionId?`\uFF0CsessionId=${s.sessionId}`:"";return `Memory distill checkpoint: ${s.pendingEvents} \u4E2A\u4E8B\u4EF6\u5F85\u63D0\u70BC${r}\u3002\u4E0A\u4E0B\u6587\u538B\u7F29\u5B8C\u6210\u540E\uFF0C\u8C03\u7528 mcp__devflow__memory_request_distill\uFF1B\u6309\u8FD4\u56DE\u7684 nextPage \u5206\u9875\u8BFB\u53D6\u5168\u90E8\u4E8B\u4EF6\uFF0C\u63D0\u70BC observations\uFF0C\u518D\u8C03\u7528 mcp__devflow__memory_save_distilled \u5E76\u786E\u8BA4\u4FDD\u5B58\u7ED3\u679C\u3002`}var w="WARNING: DevFlow is in degraded mode (daemon unreachable): 4-Gate enforcement and memory prefetch cache are unavailable. Run `devflow doctor` for details.";function E(){return process.env.DEVFLOW_HOOK_DEGRADED==="1"}async function j(s="",r=d()){let e;try{let i=JSON.parse(s);typeof i.session_id=="string"&&i.session_id.trim()&&(e=i.session_id.trim());}catch{}let t=new m;try{await t.flushAndAggregate();}finally{t.close();}let o=await _({projectRoot:r,sessionId:e,trigger:"pre_compact"}),n=[E()?w:"",b(o)].filter(Boolean).join(`
|
|
2
2
|
|
|
3
|
-
`);return {hookSpecificOutput:{hookEventName:"PreCompact",...n?{additionalContext:n}:{}}}}if(process.argv[1]?.endsWith("pre-compact")||process.argv[1]?.endsWith("pre-compact.js")){let s=console.log,
|
|
3
|
+
`);return {hookSpecificOutput:{hookEventName:"PreCompact",...n?{additionalContext:n}:{}}}}if(process.argv[1]?.endsWith("pre-compact")||process.argv[1]?.endsWith("pre-compact.js")){let s=console.log,r=console.info,e=()=>{console.log=s,console.info=r;};console.log=console.error.bind(console),console.info=console.error.bind(console);let t="";process.stdin.setEncoding("utf8"),process.stdin.on("data",o=>{t+=o;}),process.stdin.on("end",()=>{j(t.trim()||process.argv[2]||"").then(o=>{e(),s(JSON.stringify(o)),process.exit(0);}).catch(()=>{e(),process.exit(0);});}),process.stdin.on("error",()=>{e(),process.exit(0);}),setTimeout(()=>process.exit(0),14e3).unref();}export{j as handlePreCompact};
|