shennian 0.3.62 → 0.3.63

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.
@@ -346,8 +346,8 @@
346
346
  },
347
347
  {
348
348
  "file": "src/native-fusion/service.js",
349
- "beforeBytes": 25264,
350
- "afterBytes": 11871
349
+ "beforeBytes": 26155,
350
+ "afterBytes": 12343
351
351
  },
352
352
  {
353
353
  "file": "src/native-fusion/session-header.js",
@@ -7,6 +7,7 @@ export declare class NativeSessionFusionService {
7
7
  private activeTimer;
8
8
  private scanPromise;
9
9
  private queuedTargetFiles;
10
+ private pendingBoundaryFiles;
10
11
  private fullScanQueued;
11
12
  private watchEnabled;
12
13
  private lifecycleRevision;
@@ -1,2 +1,2 @@
1
- import A from"node:fs";import C from"node:crypto";import{publishPendingSessionIndexes as _}from"../session/index-publisher.js";import{NativeFusionFileWatcher as N}from"./file-watcher.js";import{NativeFusionLocalRepository as O}from"./local-repository.js";import{listClaudeTranscriptFiles as P,listCodexRolloutFiles as R,listOpenCodeSessionFiles as k,inspectNativeSessionFile as q,lookupCodexThreadName as F,parseClaudeTranscriptChunk as W,parseCodexRolloutChunk as L,parseOpenCodeSessionFile as B}from"./parsers.js";import{loadNativeScannerState as D,saveNativeScannerState as $}from"./state.js";const E=6e4,U=E*2,z=30*6e4,H=2e3,Q=3e4,K=240*6e4;function V(d){return d.replace(/\r\n/g,`
2
- `).trim()}function j(d){return d.split(/[\\/]+/).includes(".codex")}class ie{client;repository;timer=null;activeTimer=null;scanPromise=null;queuedTargetFiles=new Set;fullScanQueued=!1;watchEnabled=!1;lifecycleRevision=0;watcher=new N(e=>{this.scanNow(e).catch(s=>{console.error("[native-fusion] watched file scan failed",s)})});pendingClaims;suppressionWindows;externalObservations=new Map;constructor(e,s=new O){this.client=e,this.repository=s,this.pendingClaims=new Map((this.repository.listManagedClaims?.()??[]).map(t=>[t.canonicalMessageId,t])),this.suppressionWindows=new Map((this.repository.listManagedSuppressions?.()??[]).map(t=>[T(t.agentType,t.sourceSessionKey),t]))}start(){this.timer||(this.timer=setInterval(()=>{this.scanNow().catch(e=>{console.error("[native-fusion] periodic scan failed",e)})},E))}stop(){this.lifecycleRevision+=1,this.timer&&clearInterval(this.timer),this.timer=null,this.activeTimer&&clearInterval(this.activeTimer),this.activeTimer=null,this.watchEnabled=!1,this.watcher.stop()}handleConnected(){const e=++this.lifecycleRevision;this.watchEnabled=!0,this.scanNow().catch(s=>{console.error("[native-fusion] initial scan failed",s)}).finally(()=>{this.lifecycleRevision===e&&this.start()})}registerManagedSend(e){if(!e.canonicalMessageId)return;const s=e.sourceAgentType??e.agentType;if(s!=="codex"&&s!=="claude"&&s!=="opencode")return;const t=e.canonicalMessageId,n=this.pendingClaims.get(t),o=b(e.text);if(n&&(n.sessionId!==e.sessionId||n.sourceAgentType!==s||n.textHash!==o))throw new Error(`managed echo claim identity conflict: ${t}`);const l={sessionId:e.sessionId,agentType:e.agentType,sourceAgentType:s,canonicalMessageId:e.canonicalMessageId,textHash:o,createdAt:n?.createdAt??Date.now(),sourceSessionKey:e.sourceSessionKey??n?.sourceSessionKey??null,managedEchoPolicy:e.managedEchoPolicy??"suppress_following",externalRunId:e.externalRunId??null};if(this.repository.upsertManagedClaim?.(l),this.pendingClaims.set(t,l),e.externalRunId&&e.sourceSessionKey){const h=`${s}:${e.sourceSessionKey}`,f=this.externalObservations.get(h)??[];f.push({sessionId:e.sessionId,sourceSessionKey:e.sourceSessionKey,runId:e.externalRunId,canonicalMessageId:e.canonicalMessageId,state:"queued",createdAt:Date.now(),lastPublishedAt:Date.now()}),this.externalObservations.set(h,f),this.emitExternalActivity(f[f.length-1],"queued"),this.ensureActiveScanning()}}cancelManagedSend(e){this.repository.removeManagedClaim?.(e),this.pendingClaims.delete(e)}noteManagedSourceSession(e,s,t){if(t){this.repository.updateManagedClaimSource?.(e,s,t);for(const n of this.pendingClaims.values())n.sessionId===e&&n.sourceAgentType===s&&(n.sourceSessionKey=t)}}handleSessionDeleted(e){for(const[s,t]of this.pendingClaims.entries())t.sessionId===e&&this.pendingClaims.delete(s);for(const[s,t]of this.suppressionWindows.entries())t.sessionId===e&&this.suppressionWindows.delete(s);for(const[s,t]of this.externalObservations.entries()){const n=t.filter(o=>o.sessionId!==e);n.length===0?this.externalObservations.delete(s):this.externalObservations.set(s,n)}this.repository.purgeSession(e)}ensureActiveScanning(){this.activeTimer||(this.activeTimer=setInterval(()=>{if(this.externalObservations.size===0){this.activeTimer&&clearInterval(this.activeTimer),this.activeTimer=null;return}this.scanNow().catch(e=>{console.error("[native-fusion] active observation scan failed",e)})},H),this.scanNow().catch(()=>{}))}emitExternalActivity(e,s){this.client.sendAgentEvent({type:"event",event:"agent",id:`external-owner-${e.runId}-${s}-${e.lastPublishedAt}`,payload:{state:s==="queued"?"start":"heartbeat",sessionId:e.sessionId,runId:e.runId,seq:s==="queued"?0:1,runPhase:"thinking",canStop:!1,owner:"external",ownerLabel:"Codex Desktop",lifecycle:s}})}emitExternalTerminal(e){this.client.sendAgentEvent({type:"event",event:"agent",id:`external-owner-${e.runId}-final`,payload:{state:"final",sessionId:e.sessionId,runId:e.runId,seq:2,owner:"external",ownerLabel:"Codex Desktop"}})}pruneState(){const e=Date.now();for(const[s,t]of this.suppressionWindows.entries())t.endsAt>e||(this.repository.removeManagedSuppression?.(t.agentType,t.sourceSessionKey),this.suppressionWindows.delete(s))}async scanNow(e){if(this.client.getState()==="connected"){if(this.scanPromise){const s=this.scanPromise;if(e)for(const t of e)this.queuedTargetFiles.add(t);else this.fullScanQueued=!0;if(await s,this.fullScanQueued)return this.fullScanQueued=!1,this.queuedTargetFiles.clear(),this.scanNow();if(this.queuedTargetFiles.size>0){const t=[...this.queuedTargetFiles];return this.queuedTargetFiles.clear(),this.scanNow(t)}return}return this.scanPromise=this.runScanLoop(e).finally(()=>{this.scanPromise=null}),this.scanPromise}}async runScanLoop(e){let s=e?[...new Set(e)]:void 0;for(;;){if(await this.runScan(s),this.fullScanQueued){this.fullScanQueued=!1,this.queuedTargetFiles.clear(),s=void 0;continue}if(this.queuedTargetFiles.size>0){s=[...this.queuedTargetFiles],this.queuedTargetFiles.clear();continue}return}}async runScan(e){this.pruneState(),this.refreshExternalObservations();const s=D(),t={...s.files},n=[],o=new Map,l=new Map,h=new Map,f=e?[...e]:[...R(),...P(),...k()];!e&&this.watchEnabled&&this.watcher.refresh(f);for(const i of f){if(!A.existsSync(i))continue;const r=A.statSync(i),m=j(i),p=i.endsWith(".opencode-session.json"),c=q(i);if(c.status==="pending")continue;let u=s.files[i];const v=`${String(r.dev)}:${String(r.ino)}:${String(r.birthtimeMs)}`;let I=!u;if(u&&!p&&(r.size<u.offset||u.fileIdentity&&u.fileIdentity!==v)&&(u=void 0,I=!0),!p&&c.sources.length>0&&c.sources.every(a=>this.repository.isSourceDeleted?.(a.agentType,a.sourceSessionKey))){t[i]={...u??{daemonInstallationId:s.daemonInstallationId,classification:c.createdAtMs>=s.fusionEpochMs?"live":"historical",awaitingUserBoundary:!0,sessionCreatedAtMs:c.createdAtMs,sourceBoundaryEstablished:{}},offset:r.size,mtimeMs:r.mtimeMs,fileIdentity:v};continue}if(!u)u={daemonInstallationId:s.daemonInstallationId,offset:0,mtimeMs:r.mtimeMs,classification:c.createdAtMs>=s.fusionEpochMs?"live":"historical",awaitingUserBoundary:!0,sessionCreatedAtMs:c.createdAtMs,sourceBoundaryEstablished:{}};else if(p&&u.mtimeMs===r.mtimeMs)continue;const x=p?0:u.offset,g=p?B(i,x):m?L(i,x):W(i,x);for(const a of g.events)this.repository.isSourceDeleted?.(a.agentType,a.sourceSessionKey)||a.title?.trim()&&a.titleSource==="agent_native"&&h.set(`${a.agentType}:${a.sourceSessionKey}`,a);let w,S;if(p){const a=G(g.events,u,s.fusionEpochMs,I,r.mtimeMs,g.nextOffset);w=a.events,S=a.cursor}else if(I&&u.classification==="historical")w=[],S={...u,offset:g.nextOffset,mtimeMs:r.mtimeMs,awaitingUserBoundary:!0};else{const a=X(g.events,u,g.nextOffset,r.mtimeMs);w=a.events,S=a.cursor}S.fileIdentity=v,t[i]=S;for(const a of w){if(this.repository.isSourceDeleted?.(a.agentType,a.sourceSessionKey))continue;const y=this.tryClaimManagedEcho(a);if(y){n.push(y.event),o.set(y.claimKey,y.suppression??null),y.suppression&&l.set(T(y.suppression.agentType,y.suppression.sourceSessionKey),{window:y.suppression,claimKey:y.claimKey});continue}const M=this.suppressionFor(a,l,o);if(M){n.push({...a,claimSessionId:M.sessionId,localDisposition:"managed_echo_suppressed"});continue}n.push(a)}}this.repository.commitEvents(n);for(const[i,r]of o)this.repository.completeManagedClaim?.(i,r??void 0),this.pendingClaims.delete(i),r&&this.suppressionWindows.set(T(r.agentType,r.sourceSessionKey),r);for(const i of h.values())this.repository.observeNativeTitle(i.agentType,i.sourceSessionKey,i.title??"");for(const i of this.repository.listSourceBindings()){if(i.agentType!=="codex")continue;const r=F(i.sourceSessionKey);r&&this.repository.observeNativeTitle("codex",i.sourceSessionKey,r)}for(const i of n)this.observeExternalLifecycle(i);s.files=t,$(s),this.repository.repairMissingUserBoundaries?.(),await _(this.client).catch(i=>{console.error("[native-fusion] index projection sync failed; retained for retry",i)})}refreshExternalObservations(){const e=Date.now();for(const[s,t]of this.externalObservations.entries()){const n=t.filter(l=>e-l.createdAt<K);if(n.length===0){this.externalObservations.delete(s);continue}this.externalObservations.set(s,n);const o=n[0];e-o.lastPublishedAt<Q||(o.lastPublishedAt=e,this.emitExternalActivity(o,o.state))}}tryClaimManagedEcho(e){const s=Date.now();for(const[t,n]of this.pendingClaims.entries()){if(n.sourceAgentType!==e.agentType||e.role!=="user"||!n.sourceSessionKey||n.sourceSessionKey!==e.sourceSessionKey)continue;const o=n.externalRunId?K:U;if(Math.abs(e.ts-n.createdAt)>o||b(e.payload)!==n.textHash)continue;const l=n.managedEchoPolicy==="suppress_following"?{sessionId:n.sessionId,agentType:e.agentType,sourceSessionKey:e.sourceSessionKey,claimSourceEventKey:e.sourceEventKey,startedAt:e.ts,endsAt:Math.max(s,e.ts)+z}:void 0;return{claimKey:t,suppression:l,event:{...e,sourceMode:"managed_echo_claim",claimCanonicalMessageId:n.canonicalMessageId,claimSessionId:n.sessionId,managedEchoPolicy:n.managedEchoPolicy}}}return null}observeExternalLifecycle(e){const s=`${e.agentType}:${e.sourceSessionKey}`,t=this.externalObservations.get(s);if(!t?.length)return;const n=t[0];e.claimCanonicalMessageId===n.canonicalMessageId&&n.state==="queued"&&(n.state="running",n.lastPublishedAt=Date.now(),this.emitExternalActivity(n,"running")),!(!e.terminal||n.state!=="running")&&(this.emitExternalTerminal(n),t.shift(),t.length===0?this.externalObservations.delete(s):(this.externalObservations.set(s,t),t[0].lastPublishedAt=Date.now(),this.emitExternalActivity(t[0],"queued")))}suppressionFor(e,s,t){const n=T(e.agentType,e.sourceSessionKey),o=s.get(n),l=o?.window??this.suppressionWindows.get(n);return!l||e.ts<l.startedAt?null:e.ts>l.endsAt?(this.clearSuppression(n,l,o,s,t),null):e.role==="user"?(e.sourceEventKey===l.claimSourceEventKey||this.clearSuppression(n,l,o,s,t),null):l}clearSuppression(e,s,t,n,o){if(t){n.delete(e),o.set(t.claimKey,null);return}this.repository.removeManagedSuppression?.(s.agentType,s.sourceSessionKey),this.suppressionWindows.delete(e)}}function b(d){return C.createHash("sha256").update(V(d)).digest("hex")}function T(d,e){return`${d}:${e}`}function X(d,e,s,t){if(!e.awaitingUserBoundary)return{events:d,cursor:{...e,offset:s,mtimeMs:t}};if(d.length===0)return{events:[],cursor:{...e,offset:s,mtimeMs:t,awaitingUserBoundary:!0}};const n=d.findIndex(o=>o.role==="user");return n<0?{events:[],cursor:{...e,mtimeMs:t,awaitingUserBoundary:!0}}:{events:d.slice(n),cursor:{...e,offset:s,mtimeMs:t,awaitingUserBoundary:!1}}}function G(d,e,s,t,n,o){const l={...e.sourceBoundaryEstablished??{}},h=e.eventWatermarkTs??-1,f=new Set(e.eventKeysAtWatermark??[]),i=t?d:d.filter(c=>c.ts>h||c.ts===h&&!f.has(c.sourceEventKey)),r=[];for(const c of i){const u=c.sourceSessionKey;if(l[u]){r.push(c);continue}!(!t||(c.sourceSessionCreatedAtMs??0)>=s)||c.role!=="user"||(l[u]=!0,r.push(c))}const m=d.reduce((c,u)=>Math.max(c,u.ts),h),p=new Set(m===h?e.eventKeysAtWatermark??[]:[]);for(const c of d)c.ts===m&&p.add(c.sourceEventKey);return{events:r,cursor:{...e,offset:o,mtimeMs:n,eventWatermarkTs:m>=0?m:void 0,eventKeysAtWatermark:m>=0?[...p]:void 0,sourceBoundaryEstablished:l}}}export{ie as NativeSessionFusionService};
1
+ import A from"node:fs";import C from"node:crypto";import{publishPendingSessionIndexes as _}from"../session/index-publisher.js";import{NativeFusionFileWatcher as F}from"./file-watcher.js";import{NativeFusionLocalRepository as N}from"./local-repository.js";import{listClaudeTranscriptFiles as B,listCodexRolloutFiles as O,listOpenCodeSessionFiles as P,inspectNativeSessionFile as R,lookupCodexThreadName as k,parseClaudeTranscriptChunk as q,parseCodexRolloutChunk as W,parseOpenCodeSessionFile as L}from"./parsers.js";import{loadNativeScannerState as D,saveNativeScannerState as $}from"./state.js";const E=6e4,U=E*2,z=30*6e4,H=2e3,Q=3e4,b=240*6e4;function V(d){return d.replace(/\r\n/g,`
2
+ `).trim()}function j(d){return d.split(/[\\/]+/).includes(".codex")}class ne{client;repository;timer=null;activeTimer=null;scanPromise=null;queuedTargetFiles=new Set;pendingBoundaryFiles=new Set;fullScanQueued=!1;watchEnabled=!1;lifecycleRevision=0;watcher=new F(e=>{this.scanNow(e).catch(s=>{console.error("[native-fusion] watched file scan failed",s)})});pendingClaims;suppressionWindows;externalObservations=new Map;constructor(e,s=new N){this.client=e,this.repository=s,this.pendingClaims=new Map((this.repository.listManagedClaims?.()??[]).map(t=>[t.canonicalMessageId,t])),this.suppressionWindows=new Map((this.repository.listManagedSuppressions?.()??[]).map(t=>[T(t.agentType,t.sourceSessionKey),t]))}start(){this.timer||(this.timer=setInterval(()=>{this.scanNow().catch(e=>{console.error("[native-fusion] periodic scan failed",e)})},E))}stop(){this.lifecycleRevision+=1,this.timer&&clearInterval(this.timer),this.timer=null,this.activeTimer&&clearInterval(this.activeTimer),this.activeTimer=null,this.pendingBoundaryFiles.clear(),this.watchEnabled=!1,this.watcher.stop()}handleConnected(){const e=++this.lifecycleRevision;this.watchEnabled=!0,this.scanNow().catch(s=>{console.error("[native-fusion] initial scan failed",s)}).finally(()=>{this.lifecycleRevision===e&&this.start()})}registerManagedSend(e){if(!e.canonicalMessageId)return;const s=e.sourceAgentType??e.agentType;if(s!=="codex"&&s!=="claude"&&s!=="opencode")return;const t=e.canonicalMessageId,i=this.pendingClaims.get(t),o=K(e.text);if(i&&(i.sessionId!==e.sessionId||i.sourceAgentType!==s||i.textHash!==o))throw new Error(`managed echo claim identity conflict: ${t}`);const l={sessionId:e.sessionId,agentType:e.agentType,sourceAgentType:s,canonicalMessageId:e.canonicalMessageId,textHash:o,createdAt:i?.createdAt??Date.now(),sourceSessionKey:e.sourceSessionKey??i?.sourceSessionKey??null,managedEchoPolicy:e.managedEchoPolicy??"suppress_following",externalRunId:e.externalRunId??null};if(this.repository.upsertManagedClaim?.(l),this.pendingClaims.set(t,l),e.externalRunId&&e.sourceSessionKey){const h=`${s}:${e.sourceSessionKey}`,f=this.externalObservations.get(h)??[];f.push({sessionId:e.sessionId,sourceSessionKey:e.sourceSessionKey,runId:e.externalRunId,canonicalMessageId:e.canonicalMessageId,state:"queued",createdAt:Date.now(),lastPublishedAt:Date.now()}),this.externalObservations.set(h,f),this.emitExternalActivity(f[f.length-1],"queued"),this.ensureActiveScanning()}}cancelManagedSend(e){this.repository.removeManagedClaim?.(e),this.pendingClaims.delete(e)}noteManagedSourceSession(e,s,t){if(t){this.repository.updateManagedClaimSource?.(e,s,t);for(const i of this.pendingClaims.values())i.sessionId===e&&i.sourceAgentType===s&&(i.sourceSessionKey=t)}}handleSessionDeleted(e){for(const[s,t]of this.pendingClaims.entries())t.sessionId===e&&this.pendingClaims.delete(s);for(const[s,t]of this.suppressionWindows.entries())t.sessionId===e&&this.suppressionWindows.delete(s);for(const[s,t]of this.externalObservations.entries()){const i=t.filter(o=>o.sessionId!==e);i.length===0?this.externalObservations.delete(s):this.externalObservations.set(s,i)}this.repository.purgeSession(e)}ensureActiveScanning(e=!0){this.activeTimer||(this.activeTimer=setInterval(()=>{if(this.externalObservations.size===0&&this.pendingBoundaryFiles.size===0){this.activeTimer&&clearInterval(this.activeTimer),this.activeTimer=null;return}const s=this.externalObservations.size>0?void 0:[...this.pendingBoundaryFiles];this.scanNow(s).catch(t=>{console.error("[native-fusion] active observation scan failed",t)})},H),e&&this.scanNow().catch(()=>{}))}emitExternalActivity(e,s){this.client.sendAgentEvent({type:"event",event:"agent",id:`external-owner-${e.runId}-${s}-${e.lastPublishedAt}`,payload:{state:s==="queued"?"start":"heartbeat",sessionId:e.sessionId,runId:e.runId,seq:s==="queued"?0:1,runPhase:"thinking",canStop:!1,owner:"external",ownerLabel:"Codex Desktop",lifecycle:s}})}emitExternalTerminal(e){this.client.sendAgentEvent({type:"event",event:"agent",id:`external-owner-${e.runId}-final`,payload:{state:"final",sessionId:e.sessionId,runId:e.runId,seq:2,owner:"external",ownerLabel:"Codex Desktop"}})}pruneState(){const e=Date.now();for(const[s,t]of this.suppressionWindows.entries())t.endsAt>e||(this.repository.removeManagedSuppression?.(t.agentType,t.sourceSessionKey),this.suppressionWindows.delete(s))}async scanNow(e){if(this.client.getState()==="connected"){if(this.scanPromise){const s=this.scanPromise;if(e)for(const t of e)this.queuedTargetFiles.add(t);else this.fullScanQueued=!0;if(await s,this.fullScanQueued)return this.fullScanQueued=!1,this.queuedTargetFiles.clear(),this.scanNow();if(this.queuedTargetFiles.size>0){const t=[...this.queuedTargetFiles];return this.queuedTargetFiles.clear(),this.scanNow(t)}return}return this.scanPromise=this.runScanLoop(e).finally(()=>{this.scanPromise=null}),this.scanPromise}}async runScanLoop(e){let s=e?[...new Set(e)]:void 0;for(;;){if(await this.runScan(s),this.fullScanQueued){this.fullScanQueued=!1,this.queuedTargetFiles.clear(),s=void 0;continue}if(this.queuedTargetFiles.size>0){s=[...this.queuedTargetFiles],this.queuedTargetFiles.clear();continue}return}}async runScan(e){this.pruneState(),this.refreshExternalObservations();const s=D(),t={...s.files},i=[],o=new Map,l=new Map,h=new Map,f=e?[...e]:[...O(),...B(),...P()];!e&&this.watchEnabled&&this.watcher.refresh(f);for(const n of f){if(!A.existsSync(n)){this.pendingBoundaryFiles.delete(n);continue}const r=A.statSync(n),g=j(n),p=n.endsWith(".opencode-session.json"),c=R(n);if(c.status==="pending"){r.birthtimeMs>=s.fusionEpochMs&&this.pendingBoundaryFiles.add(n);continue}let u=s.files[n];const v=`${String(r.dev)}:${String(r.ino)}:${String(r.birthtimeMs)}`;let I=!u;if(u&&!p&&(r.size<u.offset||u.fileIdentity&&u.fileIdentity!==v)&&(u=void 0,I=!0),!p&&c.sources.length>0&&c.sources.every(a=>this.repository.isSourceDeleted?.(a.agentType,a.sourceSessionKey))){t[n]={...u??{daemonInstallationId:s.daemonInstallationId,classification:c.createdAtMs>=s.fusionEpochMs?"live":"historical",awaitingUserBoundary:!0,sessionCreatedAtMs:c.createdAtMs,sourceBoundaryEstablished:{}},offset:r.size,mtimeMs:r.mtimeMs,fileIdentity:v};continue}if(!u)u={daemonInstallationId:s.daemonInstallationId,offset:0,mtimeMs:r.mtimeMs,classification:c.createdAtMs>=s.fusionEpochMs?"live":"historical",awaitingUserBoundary:!0,sessionCreatedAtMs:c.createdAtMs,sourceBoundaryEstablished:{}};else if(p&&u.mtimeMs===r.mtimeMs)continue;const x=p?0:u.offset,S=p?L(n,x):g?W(n,x):q(n,x);for(const a of S.events)this.repository.isSourceDeleted?.(a.agentType,a.sourceSessionKey)||a.title?.trim()&&a.titleSource==="agent_native"&&h.set(`${a.agentType}:${a.sourceSessionKey}`,a);let w,m;if(p){const a=G(S.events,u,s.fusionEpochMs,I,r.mtimeMs,S.nextOffset);w=a.events,m=a.cursor}else if(I&&u.classification==="historical")w=[],m={...u,offset:S.nextOffset,mtimeMs:r.mtimeMs,awaitingUserBoundary:!0};else{const a=X(S.events,u,S.nextOffset,r.mtimeMs);w=a.events,m=a.cursor}m.fileIdentity=v,t[n]=m,m.classification==="live"&&m.awaitingUserBoundary?this.pendingBoundaryFiles.add(n):this.pendingBoundaryFiles.delete(n);for(const a of w){if(this.repository.isSourceDeleted?.(a.agentType,a.sourceSessionKey))continue;const y=this.tryClaimManagedEcho(a);if(y){i.push(y.event),o.set(y.claimKey,y.suppression??null),y.suppression&&l.set(T(y.suppression.agentType,y.suppression.sourceSessionKey),{window:y.suppression,claimKey:y.claimKey});continue}const M=this.suppressionFor(a,l,o);if(M){i.push({...a,claimSessionId:M.sessionId,localDisposition:"managed_echo_suppressed"});continue}i.push(a)}}this.repository.commitEvents(i);for(const[n,r]of o)this.repository.completeManagedClaim?.(n,r??void 0),this.pendingClaims.delete(n),r&&this.suppressionWindows.set(T(r.agentType,r.sourceSessionKey),r);for(const n of h.values())this.repository.observeNativeTitle(n.agentType,n.sourceSessionKey,n.title??"");for(const n of this.repository.listSourceBindings()){if(n.agentType!=="codex")continue;const r=k(n.sourceSessionKey);r&&this.repository.observeNativeTitle("codex",n.sourceSessionKey,r)}for(const n of i)this.observeExternalLifecycle(n);s.files=t,$(s),this.pendingBoundaryFiles.size>0&&this.ensureActiveScanning(!1),this.repository.repairMissingUserBoundaries?.(),await _(this.client).catch(n=>{console.error("[native-fusion] index projection sync failed; retained for retry",n)})}refreshExternalObservations(){const e=Date.now();for(const[s,t]of this.externalObservations.entries()){const i=t.filter(l=>e-l.createdAt<b);if(i.length===0){this.externalObservations.delete(s);continue}this.externalObservations.set(s,i);const o=i[0];e-o.lastPublishedAt<Q||(o.lastPublishedAt=e,this.emitExternalActivity(o,o.state))}}tryClaimManagedEcho(e){const s=Date.now();for(const[t,i]of this.pendingClaims.entries()){if(i.sourceAgentType!==e.agentType||e.role!=="user"||!i.sourceSessionKey||i.sourceSessionKey!==e.sourceSessionKey)continue;const o=i.externalRunId?b:U;if(Math.abs(e.ts-i.createdAt)>o||K(e.payload)!==i.textHash)continue;const l=i.managedEchoPolicy==="suppress_following"?{sessionId:i.sessionId,agentType:e.agentType,sourceSessionKey:e.sourceSessionKey,claimSourceEventKey:e.sourceEventKey,startedAt:e.ts,endsAt:Math.max(s,e.ts)+z}:void 0;return{claimKey:t,suppression:l,event:{...e,sourceMode:"managed_echo_claim",claimCanonicalMessageId:i.canonicalMessageId,claimSessionId:i.sessionId,managedEchoPolicy:i.managedEchoPolicy}}}return null}observeExternalLifecycle(e){const s=`${e.agentType}:${e.sourceSessionKey}`,t=this.externalObservations.get(s);if(!t?.length)return;const i=t[0];e.claimCanonicalMessageId===i.canonicalMessageId&&i.state==="queued"&&(i.state="running",i.lastPublishedAt=Date.now(),this.emitExternalActivity(i,"running")),!(!e.terminal||i.state!=="running")&&(this.emitExternalTerminal(i),t.shift(),t.length===0?this.externalObservations.delete(s):(this.externalObservations.set(s,t),t[0].lastPublishedAt=Date.now(),this.emitExternalActivity(t[0],"queued")))}suppressionFor(e,s,t){const i=T(e.agentType,e.sourceSessionKey),o=s.get(i),l=o?.window??this.suppressionWindows.get(i);return!l||e.ts<l.startedAt?null:e.ts>l.endsAt?(this.clearSuppression(i,l,o,s,t),null):e.role==="user"?(e.sourceEventKey===l.claimSourceEventKey||this.clearSuppression(i,l,o,s,t),null):l}clearSuppression(e,s,t,i,o){if(t){i.delete(e),o.set(t.claimKey,null);return}this.repository.removeManagedSuppression?.(s.agentType,s.sourceSessionKey),this.suppressionWindows.delete(e)}}function K(d){return C.createHash("sha256").update(V(d)).digest("hex")}function T(d,e){return`${d}:${e}`}function X(d,e,s,t){if(!e.awaitingUserBoundary)return{events:d,cursor:{...e,offset:s,mtimeMs:t}};if(d.length===0)return{events:[],cursor:{...e,offset:s,mtimeMs:t,awaitingUserBoundary:!0}};const i=d.findIndex(o=>o.role==="user");return i<0?{events:[],cursor:{...e,mtimeMs:t,awaitingUserBoundary:!0}}:{events:d.slice(i),cursor:{...e,offset:s,mtimeMs:t,awaitingUserBoundary:!1}}}function G(d,e,s,t,i,o){const l={...e.sourceBoundaryEstablished??{}},h=e.eventWatermarkTs??-1,f=new Set(e.eventKeysAtWatermark??[]),n=t?d:d.filter(c=>c.ts>h||c.ts===h&&!f.has(c.sourceEventKey)),r=[];for(const c of n){const u=c.sourceSessionKey;if(l[u]){r.push(c);continue}!(!t||(c.sourceSessionCreatedAtMs??0)>=s)||c.role!=="user"||(l[u]=!0,r.push(c))}const g=d.reduce((c,u)=>Math.max(c,u.ts),h),p=new Set(g===h?e.eventKeysAtWatermark??[]:[]);for(const c of d)c.ts===g&&p.add(c.sourceEventKey);return{events:r,cursor:{...e,offset:o,mtimeMs:i,eventWatermarkTs:g>=0?g:void 0,eventKeysAtWatermark:g>=0?[...p]:void 0,sourceBoundaryEstablished:l}}}export{ne as NativeSessionFusionService};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shennian",
3
- "version": "0.3.62",
3
+ "version": "0.3.63",
4
4
  "description": "Shennian — AI Agent Control Plane CLI",
5
5
  "type": "module",
6
6
  "bin": {