shennian 0.3.70 → 0.3.71

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.
@@ -591,8 +591,8 @@
591
591
  },
592
592
  {
593
593
  "file": "src/session/history-subscriptions.js",
594
- "beforeBytes": 8974,
595
- "afterBytes": 4411
594
+ "beforeBytes": 11172,
595
+ "afterBytes": 5401
596
596
  },
597
597
  {
598
598
  "file": "src/session/index-publisher.js",
@@ -1,6 +1,8 @@
1
1
  import { type ReqFrame } from '@shennian/wire';
2
2
  import type { CliRelayClient } from '../relay/client.js';
3
3
  import type { CanonicalSessionStore } from './canonical-store.js';
4
+ export declare const PERSONAL_HISTORY_DELTA_BATCH_WINDOW_MS = 100;
5
+ export declare const PERSONAL_HISTORY_DELTA_BATCH_MAX_MUTATIONS: 200;
4
6
  export declare class PersonalHistorySubscriptions {
5
7
  private client;
6
8
  private store;
@@ -14,6 +16,7 @@ export declare class PersonalHistorySubscriptions {
14
16
  closeAll(): void;
15
17
  closeSession(sessionId: string): void;
16
18
  private handleMutation;
17
- private publishMutation;
19
+ private queueMutation;
20
+ private flushSubscription;
18
21
  private requireSubscription;
19
22
  }
@@ -1 +1 @@
1
- import{SESSION_HISTORY_LIMITS as d}from"@shennian/wire";import{getCanonicalSessionStore as b,listSessionRecords as f}from"./store.js";import{recordPersonalSyncLocalMetric as n,setPersonalSyncLocalSubscriptions as a}from"../personal-sync/metrics.js";import{assertPersonalSyncIdentity as l,getPersonalSyncIdentity as c}from"../personal-sync/epoch.js";class m{client;store;hasLocalSession;subscriptions=new Map;unsubscribeStore;constructor(i,s=b(),o=e=>f().some(t=>t.sessionId===e)){this.client=i,this.store=s,this.hasLocalSession=o,this.unsubscribeStore=s.subscribe(e=>this.handleMutation(e))}handleOpen(i){const s=i.params;if(l(s),u(s.sessionId,s.subscriptionId),!this.hasLocalSession(s.sessionId)&&!this.store.getManifest(s.sessionId))throw new Error("history_unavailable");if(this.subscriptions.has(s.subscriptionId))throw new Error("subscription already exists");if([...this.subscriptions.values()].filter(t=>t.sessionId===s.sessionId).length>=d.maxSubscriptionsPerSession){this.client.sendRes({type:"res",id:i.id,ok:!1,error:"subscription_limit_exceeded",rejection:{code:"subscription_limit_exceeded",retryable:!0,requiredAction:"retry"}});return}const e={sessionId:s.sessionId,subscriptionId:s.subscriptionId,snapshotRevision:0,lastSentRevision:0,active:!1,buffered:[]};this.subscriptions.set(s.subscriptionId,e),a(this.subscriptions.size);try{const t=this.store.readPage(s.sessionId,{limit:p(s.limit,d.defaultSnapshotMessages),...s.beforeSequence===void 0?{}:{beforeSequence:s.beforeSequence}});e.snapshotRevision=t.bodyRevision,e.lastSentRevision=t.bodyRevision,this.client.sendRes({type:"res",id:i.id,ok:!0,payload:{...c(this.store.daemonInstallationId),sessionId:s.sessionId,subscriptionId:s.subscriptionId,storageMode:"machine_local",snapshotRevision:t.bodyRevision,firstAvailableSeq:t.firstAvailableSeq,lastAvailableSeq:t.lastAvailableSeq,hasOlder:t.hasOlder,messages:t.entries}}),n("historySnapshots"),n("historyBytesServed",Buffer.byteLength(JSON.stringify(t.entries),"utf8")),e.active=!0;for(const h of e.buffered)this.publishMutation(e,h);e.buffered=[]}catch(t){throw this.subscriptions.delete(s.subscriptionId),a(this.subscriptions.size),t}}handlePage(i){const s=i.params;l(s);const o=this.requireSubscription(s.sessionId,s.subscriptionId);let e;try{e=this.store.readPageAtRevision(s.sessionId,{beforeSequence:s.beforeSequence,limit:p(s.limit,d.defaultPageMessages),revision:o.snapshotRevision})}catch{throw new Error("snapshot_compacted")}this.client.sendRes({type:"res",id:i.id,ok:!0,payload:{...c(this.store.daemonInstallationId),sessionId:s.sessionId,subscriptionId:s.subscriptionId,snapshotRevision:o.snapshotRevision,firstAvailableSeq:e.firstAvailableSeq,hasOlder:e.hasOlder,messages:e.entries}}),n("historyPages"),n("historyBytesServed",Buffer.byteLength(JSON.stringify(e.entries),"utf8"))}handleClose(i){const s=i.params;l(s),u(s.sessionId,s.subscriptionId),this.subscriptions.get(s.subscriptionId)?.sessionId===s.sessionId&&(this.subscriptions.delete(s.subscriptionId),a(this.subscriptions.size)),this.client.sendRes({type:"res",id:i.id,ok:!0,payload:{...c(this.store.daemonInstallationId),sessionId:s.sessionId,subscriptionId:s.subscriptionId,closed:!0}})}closeAll(){this.subscriptions.clear(),a(0),this.unsubscribeStore()}closeSession(i){for(const[s,o]of this.subscriptions.entries())o.sessionId===i&&this.subscriptions.delete(s);a(this.subscriptions.size)}handleMutation(i){for(const s of this.subscriptions.values())s.sessionId===i.sessionId&&(s.active?this.publishMutation(s,i):s.buffered.push(i))}publishMutation(i,s){s.revision<=i.lastSentRevision||(this.client.sendEvent({type:"event",event:"session.body.delta",payload:{...c(this.store.daemonInstallationId),sessionId:i.sessionId,subscriptionId:i.subscriptionId,fromRevision:i.lastSentRevision,toRevision:s.revision,mutations:[{...s,subscriptionId:i.subscriptionId}]}}),n("historyDeltas"),n("historyBytesServed",Buffer.byteLength(JSON.stringify(s),"utf8")),i.lastSentRevision=s.revision)}requireSubscription(i,s){u(i,s);const o=this.subscriptions.get(s);if(!o||o.sessionId!==i)throw new Error("subscription_not_found");return o}}function u(r,i){if(typeof r!="string"||!r||typeof i!="string"||!i)throw new Error("invalid history subscription identity")}function p(r,i){const s=r??i;if(!Number.isSafeInteger(s)||s<1||s>d.maxPageMessages)throw new Error("invalid history limit");return s}export{m as PersonalHistorySubscriptions};
1
+ import{SESSION_HISTORY_LIMITS as o}from"@shennian/wire";import{getCanonicalSessionStore as p,listSessionRecords as b}from"./store.js";import{recordPersonalSyncLocalMetric as r,setPersonalSyncLocalSubscriptions as d}from"../personal-sync/metrics.js";import{assertPersonalSyncIdentity as l,getPersonalSyncIdentity as u}from"../personal-sync/epoch.js";const S=100,I=o.maxDeltaMutations,y=o.maxMutationBytes-4096;class _{client;store;hasLocalSession;subscriptions=new Map;unsubscribeStore;constructor(e,s=p(),t=i=>b().some(n=>n.sessionId===i)){this.client=e,this.store=s,this.hasLocalSession=t,this.unsubscribeStore=s.subscribe(i=>this.handleMutation(i))}handleOpen(e){const s=e.params;if(l(s),c(s.sessionId,s.subscriptionId),!this.hasLocalSession(s.sessionId)&&!this.store.getManifest(s.sessionId))throw new Error("history_unavailable");if(this.subscriptions.has(s.subscriptionId))throw new Error("subscription already exists");if([...this.subscriptions.values()].filter(n=>n.sessionId===s.sessionId).length>=o.maxSubscriptionsPerSession){this.client.sendRes({type:"res",id:e.id,ok:!1,error:"subscription_limit_exceeded",rejection:{code:"subscription_limit_exceeded",retryable:!0,requiredAction:"retry"}});return}const i={sessionId:s.sessionId,subscriptionId:s.subscriptionId,snapshotRevision:0,lastSentRevision:0,active:!1,buffered:[],pending:[],pendingBytes:0,flushTimer:null,lastQueuedRevision:0};this.subscriptions.set(s.subscriptionId,i),d(this.subscriptions.size);try{const n=this.store.readPage(s.sessionId,{limit:h(s.limit,o.defaultSnapshotMessages),...s.beforeSequence===void 0?{}:{beforeSequence:s.beforeSequence}});i.snapshotRevision=n.bodyRevision,i.lastSentRevision=n.bodyRevision,i.lastQueuedRevision=n.bodyRevision,this.client.sendRes({type:"res",id:e.id,ok:!0,payload:{...u(this.store.daemonInstallationId),sessionId:s.sessionId,subscriptionId:s.subscriptionId,storageMode:"machine_local",snapshotRevision:n.bodyRevision,firstAvailableSeq:n.firstAvailableSeq,lastAvailableSeq:n.lastAvailableSeq,hasOlder:n.hasOlder,messages:n.entries}}),r("historySnapshots"),r("historyBytesServed",Buffer.byteLength(JSON.stringify(n.entries),"utf8")),i.active=!0;for(const f of i.buffered)this.queueMutation(i,f);i.buffered=[]}catch(n){throw this.subscriptions.delete(s.subscriptionId),d(this.subscriptions.size),n}}handlePage(e){const s=e.params;l(s);const t=this.requireSubscription(s.sessionId,s.subscriptionId);let i;try{i=this.store.readPageAtRevision(s.sessionId,{beforeSequence:s.beforeSequence,limit:h(s.limit,o.defaultPageMessages),revision:t.snapshotRevision})}catch{throw new Error("snapshot_compacted")}this.client.sendRes({type:"res",id:e.id,ok:!0,payload:{...u(this.store.daemonInstallationId),sessionId:s.sessionId,subscriptionId:s.subscriptionId,snapshotRevision:t.snapshotRevision,firstAvailableSeq:i.firstAvailableSeq,hasOlder:i.hasOlder,messages:i.entries}}),r("historyPages"),r("historyBytesServed",Buffer.byteLength(JSON.stringify(i.entries),"utf8"))}handleClose(e){const s=e.params;l(s),c(s.sessionId,s.subscriptionId);const t=this.subscriptions.get(s.subscriptionId);t?.sessionId===s.sessionId&&(this.flushSubscription(t),this.subscriptions.delete(s.subscriptionId),d(this.subscriptions.size)),this.client.sendRes({type:"res",id:e.id,ok:!0,payload:{...u(this.store.daemonInstallationId),sessionId:s.sessionId,subscriptionId:s.subscriptionId,closed:!0}})}closeAll(){for(const e of this.subscriptions.values())this.flushSubscription(e);this.subscriptions.clear(),d(0),this.unsubscribeStore()}closeSession(e){for(const[s,t]of this.subscriptions.entries())t.sessionId===e&&(this.flushSubscription(t),this.subscriptions.delete(s));d(this.subscriptions.size)}handleMutation(e){for(const s of this.subscriptions.values())s.sessionId===e.sessionId&&(s.active?this.queueMutation(s,e):s.buffered.push(e))}queueMutation(e,s){if(s.revision<=e.lastQueuedRevision)return;const t=Buffer.byteLength(JSON.stringify({...s,subscriptionId:e.subscriptionId}),"utf8")+1;if(e.pending.length>0&&e.pendingBytes+t>y&&this.flushSubscription(e),e.pending.push(s),e.pendingBytes+=t,e.lastQueuedRevision=s.revision,e.pending.length>=I){this.flushSubscription(e);return}e.flushTimer||(e.flushTimer=setTimeout(()=>this.flushSubscription(e),S))}flushSubscription(e){if(e.flushTimer&&(clearTimeout(e.flushTimer),e.flushTimer=null),e.pending.length===0)return;const s=e.pending.filter(i=>i.revision>e.lastSentRevision);if(e.pending=[],e.pendingBytes=0,s.length===0)return;const t=s[s.length-1].revision;this.client.sendEvent({type:"event",event:"session.body.delta",payload:{...u(this.store.daemonInstallationId),sessionId:e.sessionId,subscriptionId:e.subscriptionId,fromRevision:e.lastSentRevision,toRevision:t,mutations:s.map(i=>({...i,subscriptionId:e.subscriptionId}))}}),r("historyDeltas"),r("historyBytesServed",Buffer.byteLength(JSON.stringify(s),"utf8")),e.lastSentRevision=t}requireSubscription(e,s){c(e,s);const t=this.subscriptions.get(s);if(!t||t.sessionId!==e)throw new Error("subscription_not_found");return t}}function c(a,e){if(typeof a!="string"||!a||typeof e!="string"||!e)throw new Error("invalid history subscription identity")}function h(a,e){const s=a??e;if(!Number.isSafeInteger(s)||s<1||s>o.maxPageMessages)throw new Error("invalid history limit");return s}export{I as PERSONAL_HISTORY_DELTA_BATCH_MAX_MUTATIONS,S as PERSONAL_HISTORY_DELTA_BATCH_WINDOW_MS,_ as PersonalHistorySubscriptions};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shennian",
3
- "version": "0.3.70",
3
+ "version": "0.3.71",
4
4
  "description": "Shennian — AI Agent Control Plane CLI",
5
5
  "type": "module",
6
6
  "bin": {