shennian 0.3.51 → 0.3.52

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.
@@ -596,8 +596,8 @@
596
596
  },
597
597
  {
598
598
  "file": "src/session/store.js",
599
- "beforeBytes": 16551,
600
- "afterBytes": 8868
599
+ "beforeBytes": 17364,
600
+ "afterBytes": 9292
601
601
  },
602
602
  {
603
603
  "file": "src/session/tool-detail-store.js",
@@ -19,6 +19,9 @@ export type LocalSessionRecord = {
19
19
  updatedAt: string;
20
20
  lastActivityAt: string;
21
21
  lastMessagePreview?: string | null;
22
+ localReady: boolean;
23
+ firstUserMessageId: string | null;
24
+ firstUserBodyRevision: number | null;
22
25
  bodyRevision: number;
23
26
  indexRevision: number;
24
27
  title: string;
@@ -1,2 +1,2 @@
1
- import R from"node:crypto";import r from"node:fs";import v from"node:path";import{SESSION_SYNC_PROTOCOL_VERSION as k,SESSION_TITLE_SOURCE_PRIORITY as m,sessionPreviewFromPayload as b}from"@shennian/wire";import{PERSONAL_SYNC_V1_ROOT_NAME as C,resolvePersonalSyncV1Path as O}from"../personal-sync/paths.js";import{CanonicalSessionStore as E}from"./canonical-store.js";import{getDaemonInstallationId as d}from"../personal-sync/identity.js";import{NativeSourceDeletionStore as D}from"../native-fusion/native-source-deletions.js";let y=null,x=null;function I(){return O()}function a(){const t=v.join(I(),"canonical");return(!y||x!==t)&&(y=new E(t,d()),x=t),y}function p(){return v.join(I(),"session-index.json")}function u(){const t=p();if(!r.existsSync(t))return{};const e=JSON.parse(r.readFileSync(t,"utf8"));if(!e||typeof e!="object"||Array.isArray(e))throw new Error(`invalid personal session index: ${t}`);const i=d();for(const n of Object.values(e))if(n.daemonInstallationId!==i)throw new Error(`personal session index belongs to another daemon installation: ${t}`);return e}function g(t){P(p(),t)}function _(t){return b(t.payload,120)}function S(t){if(new D({rootDir:I(),daemonInstallationId:d()}).hasSession(t))throw new Error(`session_deleted:${t}`)}function T(t){S(t.sessionId),d();const e=u(),i=e[t.sessionId],n=new Date().toISOString(),s=a().getManifest(t.sessionId)?.bodyRevision??i?.bodyRevision??0,o=a().getManifest(t.sessionId),l=U(i,t),c={daemonInstallationId:d(),sessionId:t.sessionId,agentType:t.agentType,sessionMode:t.sessionMode??i?.sessionMode,managerConfig:t.managerConfig??i?.managerConfig??null,workDir:t.workDir,agentSessionId:t.agentSessionId??i?.agentSessionId??null,modelId:t.modelId??i?.modelId??null,managerDefaultWorkerAgentType:t.managerDefaultWorkerAgentType??i?.managerDefaultWorkerAgentType??null,managerDefaultWorkerModelId:t.managerDefaultWorkerModelId??i?.managerDefaultWorkerModelId??null,status:t.status??i?.status??"active",createdAt:i?.createdAt??n,updatedAt:n,lastActivityAt:t.lastActivityAt??i?.lastActivityAt??n,lastMessagePreview:t.lastMessagePreview??i?.lastMessagePreview??null,bodyRevision:s,indexRevision:(i?.indexRevision??0)+1,...l,firstAvailableSeq:o?.firstAvailableSeq??i?.firstAvailableSeq??null,visibleMessageCount:o?.visibleMessageCount??i?.visibleMessageCount??0,lastVisibleMessageId:o?.lastVisibleMessageId??i?.lastVisibleMessageId??null,lastBodyCommittedAt:o?.lastBodyCommittedAt??i?.lastBodyCommittedAt??null,deletionRevision:i?.deletionRevision??0,deletedAt:t.deletedAt??i?.deletedAt??null,syncedIndexRevision:i?.syncedIndexRevision??0,titleGenerationAttemptedAt:i?.titleGenerationAttemptedAt??null};return e[t.sessionId]=c,g(e),c}function h(){return Object.values(u())}function F(t){if(!t)throw new Error("sessionId is required");const e=u();e[t]&&(delete e[t],g(e))}function J(t){a().purgeSession(t)}function Y(){const t=d();return h().filter(e=>e.indexRevision>e.syncedIndexRevision).sort((e,i)=>e.indexRevision-i.indexRevision).map(e=>({protocolVersion:k,daemonInstallationId:t,session:{id:e.sessionId,agentType:e.agentType,sessionMode:e.sessionMode,managerConfig:e.managerConfig,agentSessionId:e.agentSessionId??null,modelId:e.modelId??null,managerDefaultWorkerAgentType:e.managerDefaultWorkerAgentType??null,managerDefaultWorkerModelId:e.managerDefaultWorkerModelId??null,title:e.title,titleSource:e.titleSource,titleRevision:e.titleRevision,titleLockedByUser:e.titleLockedByUser,titleOriginEventId:e.titleOriginEventId,status:e.status??"active",workDir:e.workDir,lastMessagePreview:e.lastMessagePreview??null,bodyStorageMode:"machine_local",bodyRevision:e.bodyRevision,indexRevision:e.indexRevision,firstAvailableSeq:e.firstAvailableSeq,visibleMessageCount:e.visibleMessageCount,lastVisibleMessageId:e.lastVisibleMessageId,lastBodyCommittedAt:e.lastBodyCommittedAt,deletionRevision:e.deletionRevision,lastActivityAt:e.lastActivityAt,createdAt:e.createdAt,updatedAt:e.updatedAt,deletedAt:e.deletedAt}}))}function z(t,e){const i=u(),n=i[t];if(!(!n||e<=n.syncedIndexRevision)){if(e>n.indexRevision)throw new Error(`cannot acknowledge future index revision ${e} for ${t}`);i[t]={...n,syncedIndexRevision:e},g(i)}}const B=2*6e4;function H(t=Date.now(),e=B){const i=u(),n=[],s=new Date(t).toISOString();for(const o of Object.values(i)){if(o.titleSource!=="first_user_fallback"||o.titleLockedByUser||o.titleGenerationAttemptedAt||!o.title.trim())continue;const l=o.status==="completed"||o.status==="failed",c=o.visibleMessageCount>=2;let f=null;if(o.agentType==="claude"&&c?f="agent_native_unavailable":l?f="terminal":c&&t-Date.parse(o.createdAt)>=e&&(f="grace_elapsed"),!f)continue;const A=b(o.title,240);if(!A)continue;const M=R.randomUUID();i[o.sessionId]={...o,titleGenerationAttemptedAt:s},n.push({sessionId:o.sessionId,requestId:M,titleSeed:A,baseTitleRevision:o.titleRevision,eligibility:f})}return n.length>0&&g(i),n}function K(t,e){const n=u()[t];return!n||n.titleLockedByUser||m[n.titleSource]>=m.shennian_generated||e.titleRevision<=n.titleRevision?!1:(T({sessionId:t,agentType:n.agentType,sessionMode:n.sessionMode,managerConfig:n.managerConfig,workDir:n.workDir,agentSessionId:n.agentSessionId,modelId:n.modelId,status:n.status,title:e.title,titleSource:"shennian_generated",titleRevision:e.titleRevision,titleLockedByUser:!1,titleOriginEventId:e.titleOriginEventId}),!0)}function Q(t,e,i){S(t),d();const n=a().append(t,e,i);return w(t,e,n.bodyRevision),n.bodyRevision}function X(t,e,i){S(t),d();const n=a().upsert(t,e,i);return w(t,e,n.bodyRevision),n.bodyRevision}function Z(t,e){const i=a().getMessageState(t,e);return!i||i.tombstoned||!i.message||!i.deliveryState?{state:"not_found",sessionId:t,clientMessageId:e}:{state:i.deliveryState,sessionId:t,clientMessageId:e,messageId:i.message.id,deliveryState:i.deliveryState,bodyRevision:i.lastRevision,committedAt:new Date(i.message.ts).toISOString()}}function ee(t,e){S(t),d();const i=a().tombstone(t,e),n=u(),s=n[t];return s&&(n[t]={...s,bodyRevision:i.bodyRevision,indexRevision:s.indexRevision+1,updatedAt:new Date().toISOString()},g(n)),i.bodyRevision}function w(t,e,i){const n=u(),s=n[t];if(!s)return;const o=_(e),l=a().getManifest(t),c=e.role==="user"&&o&&!s.title?{title:o,titleSource:"first_user_fallback",titleRevision:s.titleRevision+1,titleLockedByUser:!1,titleOriginEventId:e.id}:null;n[t]={...s,...c??{},bodyRevision:i,indexRevision:s.indexRevision+1,updatedAt:new Date().toISOString(),lastActivityAt:new Date(e.ts).toISOString(),lastMessagePreview:o??s.lastMessagePreview??null,firstAvailableSeq:l?.firstAvailableSeq??s.firstAvailableSeq,visibleMessageCount:l?.visibleMessageCount??s.visibleMessageCount,lastVisibleMessageId:l?.lastVisibleMessageId??s.lastVisibleMessageId,lastBodyCommittedAt:l?.lastBodyCommittedAt??s.lastBodyCommittedAt},g(n)}function U(t,e){const i={title:t?.title??"",titleSource:t?.titleSource??"first_user_fallback",titleRevision:t?.titleRevision??0,titleLockedByUser:t?.titleLockedByUser??!1,titleOriginEventId:t?.titleOriginEventId??null},n=e.title?.trim()??"";if(!n)return i;if(!e.titleSource)throw new Error("titleSource is required with title");const s=e.titleSource==="shennian_manual";if(e.titleLockedByUser!==void 0&&e.titleLockedByUser!==s)throw new Error("titleLockedByUser conflicts with titleSource");const o=n===i.title&&e.titleSource===i.titleSource,l=e.titleRevision??i.titleRevision+(o?0:1);if(!Number.isSafeInteger(l)||l<0)throw new Error("invalid titleRevision");const c=m[e.titleSource],f=m[i.titleSource];return c<f||l<i.titleRevision||l===i.titleRevision&&!o?i:{title:n,titleSource:e.titleSource,titleRevision:l,titleLockedByUser:s,titleOriginEventId:e.titleOriginEventId??i.titleOriginEventId}}function te(t,e){const i=e?.limit&&e.limit>0?Math.min(e.limit,500):500;let s=a().readRecent(t,i).messages;return e?.before!==void 0&&(s=s.filter(o=>o.ts<e.before)),[...s].sort((o,l)=>l.ts-o.ts)}function ie(){return a().listSessionIds()}function ne(){return a()}function P(t,e){const i=v.dirname(t);r.mkdirSync(i,{recursive:!0,mode:448});const n=`${t}.tmp-${process.pid}-${R.randomBytes(6).toString("hex")}`;let s;try{if(s=r.openSync(n,"wx",384),r.writeFileSync(s,`${JSON.stringify(e,null,2)}
2
- `,"utf8"),r.fsyncSync(s),r.closeSync(s),s=void 0,r.renameSync(n,t),process.platform!=="win32"){const o=r.openSync(i,"r");try{r.fsyncSync(o)}finally{r.closeSync(o)}}}finally{s!==void 0&&r.closeSync(s);try{r.unlinkSync(n)}catch{}}}export{C as PERSONAL_SYNC_V1_ROOT_NAME,z as acknowledgeSessionIndexUpdate,Q as appendMessage,K as applyGeneratedSessionTitle,H as claimSessionTitleGenerationCandidates,ne as getCanonicalSessionStore,d as getDaemonInstallationId,Z as getMessageDeliveryStatus,Y as listPendingSessionIndexUpdates,h as listSessionRecords,ie as listSessions,J as purgeCanonicalSession,F as purgeSessionRecord,te as readMessages,T as recordSession,ee as tombstoneMessage,X as upsertMessage};
1
+ import A from"node:crypto";import r from"node:fs";import y from"node:path";import{SESSION_SYNC_PROTOCOL_VERSION as k,SESSION_TITLE_SOURCE_PRIORITY as m,sessionPreviewFromPayload as b}from"@shennian/wire";import{PERSONAL_SYNC_V1_ROOT_NAME as C,resolvePersonalSyncV1Path as O}from"../personal-sync/paths.js";import{CanonicalSessionStore as E}from"./canonical-store.js";import{getDaemonInstallationId as d}from"../personal-sync/identity.js";import{NativeSourceDeletionStore as U}from"../native-fusion/native-source-deletions.js";let S=null,x=null;function I(){return O()}function a(){const t=y.join(I(),"canonical");return(!S||x!==t)&&(S=new E(t,d()),x=t),S}function p(){return y.join(I(),"session-index.json")}function f(){const t=p();if(!r.existsSync(t))return{};const e=JSON.parse(r.readFileSync(t,"utf8"));if(!e||typeof e!="object"||Array.isArray(e))throw new Error(`invalid personal session index: ${t}`);const i=d();for(const n of Object.values(e))if(n.daemonInstallationId!==i)throw new Error(`personal session index belongs to another daemon installation: ${t}`);return e}function g(t){P(p(),t)}function B(t){return b(t.payload,120)}function v(t){if(new U({rootDir:I(),daemonInstallationId:d()}).hasSession(t))throw new Error(`session_deleted:${t}`)}function D(t){v(t.sessionId),d();const e=f(),i=e[t.sessionId],n=new Date().toISOString(),s=a().getManifest(t.sessionId)?.bodyRevision??i?.bodyRevision??0,o=a().getManifest(t.sessionId),l=h(i,t),c={daemonInstallationId:d(),sessionId:t.sessionId,agentType:t.agentType,sessionMode:t.sessionMode??i?.sessionMode,managerConfig:t.managerConfig??i?.managerConfig??null,workDir:t.workDir,agentSessionId:t.agentSessionId??i?.agentSessionId??null,modelId:t.modelId??i?.modelId??null,managerDefaultWorkerAgentType:t.managerDefaultWorkerAgentType??i?.managerDefaultWorkerAgentType??null,managerDefaultWorkerModelId:t.managerDefaultWorkerModelId??i?.managerDefaultWorkerModelId??null,status:t.status??i?.status??"active",createdAt:i?.createdAt??n,updatedAt:n,lastActivityAt:t.lastActivityAt??i?.lastActivityAt??n,lastMessagePreview:t.lastMessagePreview??i?.lastMessagePreview??null,localReady:i?.localReady===!0,firstUserMessageId:i?.firstUserMessageId??null,firstUserBodyRevision:i?.firstUserBodyRevision??null,bodyRevision:s,indexRevision:(i?.indexRevision??0)+1,...l,firstAvailableSeq:o?.firstAvailableSeq??i?.firstAvailableSeq??null,visibleMessageCount:o?.visibleMessageCount??i?.visibleMessageCount??0,lastVisibleMessageId:o?.lastVisibleMessageId??i?.lastVisibleMessageId??null,lastBodyCommittedAt:o?.lastBodyCommittedAt??i?.lastBodyCommittedAt??null,deletionRevision:i?.deletionRevision??0,deletedAt:t.deletedAt??i?.deletedAt??null,syncedIndexRevision:i?.syncedIndexRevision??0,titleGenerationAttemptedAt:i?.titleGenerationAttemptedAt??null};return e[t.sessionId]=c,g(e),c}function _(){return Object.values(f())}function F(t){if(!t)throw new Error("sessionId is required");const e=f();e[t]&&(delete e[t],g(e))}function J(t){a().purgeSession(t)}function Y(){const t=d();return _().filter(e=>e.indexRevision>e.syncedIndexRevision&&e.localReady&&!!e.firstUserMessageId&&e.firstUserBodyRevision!=null).sort((e,i)=>e.indexRevision-i.indexRevision).map(e=>({protocolVersion:k,daemonInstallationId:t,session:{id:e.sessionId,agentType:e.agentType,sessionMode:e.sessionMode,managerConfig:e.managerConfig,agentSessionId:e.agentSessionId??null,modelId:e.modelId??null,managerDefaultWorkerAgentType:e.managerDefaultWorkerAgentType??null,managerDefaultWorkerModelId:e.managerDefaultWorkerModelId??null,title:e.title,titleSource:e.titleSource,titleRevision:e.titleRevision,titleLockedByUser:e.titleLockedByUser,titleOriginEventId:e.titleOriginEventId,status:e.status??"active",workDir:e.workDir,lastMessagePreview:e.lastMessagePreview??null,bodyStorageMode:"machine_local",localReady:!0,firstUserBoundary:{role:"user",messageId:e.firstUserMessageId,bodyRevision:e.firstUserBodyRevision},bodyRevision:e.bodyRevision,indexRevision:e.indexRevision,firstAvailableSeq:e.firstAvailableSeq,visibleMessageCount:e.visibleMessageCount,lastVisibleMessageId:e.lastVisibleMessageId,lastBodyCommittedAt:e.lastBodyCommittedAt,deletionRevision:e.deletionRevision,lastActivityAt:e.lastActivityAt,createdAt:e.createdAt,updatedAt:e.updatedAt,deletedAt:e.deletedAt}}))}function z(t,e){const i=f(),n=i[t];if(!(!n||e<=n.syncedIndexRevision)){if(e>n.indexRevision)throw new Error(`cannot acknowledge future index revision ${e} for ${t}`);i[t]={...n,syncedIndexRevision:e},g(i)}}const T=2*6e4;function H(t=Date.now(),e=T){const i=f(),n=[],s=new Date(t).toISOString();for(const o of Object.values(i)){if(o.titleSource!=="first_user_fallback"||o.titleLockedByUser||o.titleGenerationAttemptedAt||!o.title.trim())continue;const l=o.status==="completed"||o.status==="failed",c=o.visibleMessageCount>=2;let u=null;if(o.agentType==="claude"&&c?u="agent_native_unavailable":l?u="terminal":c&&t-Date.parse(o.createdAt)>=e&&(u="grace_elapsed"),!u)continue;const R=b(o.title,240);if(!R)continue;const w=A.randomUUID();i[o.sessionId]={...o,titleGenerationAttemptedAt:s},n.push({sessionId:o.sessionId,requestId:w,titleSeed:R,baseTitleRevision:o.titleRevision,eligibility:u})}return n.length>0&&g(i),n}function K(t,e){const n=f()[t];return!n||n.titleLockedByUser||m[n.titleSource]>=m.shennian_generated||e.titleRevision<=n.titleRevision?!1:(D({sessionId:t,agentType:n.agentType,sessionMode:n.sessionMode,managerConfig:n.managerConfig,workDir:n.workDir,agentSessionId:n.agentSessionId,modelId:n.modelId,status:n.status,title:e.title,titleSource:"shennian_generated",titleRevision:e.titleRevision,titleLockedByUser:!1,titleOriginEventId:e.titleOriginEventId}),!0)}function Q(t,e,i){v(t),d();const n=a().append(t,e,i);return M(t,e,n.bodyRevision),n.bodyRevision}function X(t,e,i){v(t),d();const n=a().upsert(t,e,i);return M(t,e,n.bodyRevision),n.bodyRevision}function Z(t,e){const i=a().getMessageState(t,e);return!i||i.tombstoned||!i.message||!i.deliveryState?{state:"not_found",sessionId:t,clientMessageId:e}:{state:i.deliveryState,sessionId:t,clientMessageId:e,messageId:i.message.id,deliveryState:i.deliveryState,bodyRevision:i.lastRevision,committedAt:new Date(i.message.ts).toISOString()}}function ee(t,e){v(t),d();const i=a().tombstone(t,e),n=f(),s=n[t];return s&&(n[t]={...s,bodyRevision:i.bodyRevision,indexRevision:s.indexRevision+1,updatedAt:new Date().toISOString()},g(n)),i.bodyRevision}function M(t,e,i){const n=f(),s=n[t];if(!s)return;const o=B(e),l=a().getManifest(t),c=e.role==="user"&&o&&!s.title?{title:o,titleSource:"first_user_fallback",titleRevision:s.titleRevision+1,titleLockedByUser:!1,titleOriginEventId:e.id}:null,u=e.role==="user"&&!s.localReady?{localReady:!0,firstUserMessageId:e.id,firstUserBodyRevision:i}:null;n[t]={...s,...c??{},...u??{},bodyRevision:i,indexRevision:s.indexRevision+1,updatedAt:new Date().toISOString(),lastActivityAt:new Date(e.ts).toISOString(),lastMessagePreview:o??s.lastMessagePreview??null,firstAvailableSeq:l?.firstAvailableSeq??s.firstAvailableSeq,visibleMessageCount:l?.visibleMessageCount??s.visibleMessageCount,lastVisibleMessageId:l?.lastVisibleMessageId??s.lastVisibleMessageId,lastBodyCommittedAt:l?.lastBodyCommittedAt??s.lastBodyCommittedAt},g(n)}function h(t,e){const i={title:t?.title??"",titleSource:t?.titleSource??"first_user_fallback",titleRevision:t?.titleRevision??0,titleLockedByUser:t?.titleLockedByUser??!1,titleOriginEventId:t?.titleOriginEventId??null},n=e.title?.trim()??"";if(!n)return i;if(!e.titleSource)throw new Error("titleSource is required with title");const s=e.titleSource==="shennian_manual";if(e.titleLockedByUser!==void 0&&e.titleLockedByUser!==s)throw new Error("titleLockedByUser conflicts with titleSource");const o=n===i.title&&e.titleSource===i.titleSource,l=e.titleRevision??i.titleRevision+(o?0:1);if(!Number.isSafeInteger(l)||l<0)throw new Error("invalid titleRevision");const c=m[e.titleSource],u=m[i.titleSource];return c<u||l<i.titleRevision||l===i.titleRevision&&!o?i:{title:n,titleSource:e.titleSource,titleRevision:l,titleLockedByUser:s,titleOriginEventId:e.titleOriginEventId??i.titleOriginEventId}}function te(t,e){const i=e?.limit&&e.limit>0?Math.min(e.limit,500):500;let s=a().readRecent(t,i).messages;return e?.before!==void 0&&(s=s.filter(o=>o.ts<e.before)),[...s].sort((o,l)=>l.ts-o.ts)}function ie(){return a().listSessionIds()}function ne(){return a()}function P(t,e){const i=y.dirname(t);r.mkdirSync(i,{recursive:!0,mode:448});const n=`${t}.tmp-${process.pid}-${A.randomBytes(6).toString("hex")}`;let s;try{if(s=r.openSync(n,"wx",384),r.writeFileSync(s,`${JSON.stringify(e,null,2)}
2
+ `,"utf8"),r.fsyncSync(s),r.closeSync(s),s=void 0,r.renameSync(n,t),process.platform!=="win32"){const o=r.openSync(i,"r");try{r.fsyncSync(o)}finally{r.closeSync(o)}}}finally{s!==void 0&&r.closeSync(s);try{r.unlinkSync(n)}catch{}}}export{C as PERSONAL_SYNC_V1_ROOT_NAME,z as acknowledgeSessionIndexUpdate,Q as appendMessage,K as applyGeneratedSessionTitle,H as claimSessionTitleGenerationCandidates,ne as getCanonicalSessionStore,d as getDaemonInstallationId,Z as getMessageDeliveryStatus,Y as listPendingSessionIndexUpdates,_ as listSessionRecords,ie as listSessions,J as purgeCanonicalSession,F as purgeSessionRecord,te as readMessages,D as recordSession,ee as tombstoneMessage,X as upsertMessage};
@@ -3,13 +3,13 @@ import type { AgentType, ManagerConfig, SessionMode } from './session.ts';
3
3
  export declare const SESSION_SYNC_PROTOCOL_VERSION: 1;
4
4
  export type SessionSyncProtocolVersion = typeof SESSION_SYNC_PROTOCOL_VERSION;
5
5
  export declare const SESSION_SYNC_RELEASE_GATE: {
6
- readonly minCliVersion: "0.3.42";
6
+ readonly minCliVersion: "0.3.52";
7
7
  readonly minAppBuild: {
8
8
  readonly ios: 51;
9
9
  readonly android: 12;
10
10
  };
11
11
  };
12
- export declare const SESSION_SYNC_REQUIRED_CAPABILITIES: readonly ["session_index_v1", "session_body_revision_v1", "session_history_subscription_v1", "local_canonical_v1", "session_title_authority_v1", "session_title_seed_v1", "session_tool_detail_v1", "voice_session_body_relay_v1", "native_source_tombstone_v1", "session_durable_send_v1", "daemon_installation_identity_v1"];
12
+ export declare const SESSION_SYNC_REQUIRED_CAPABILITIES: readonly ["session_index_v1", "session_body_revision_v1", "session_history_subscription_v1", "local_canonical_v1", "session_title_authority_v1", "session_title_seed_v1", "session_tool_detail_v1", "voice_session_body_relay_v1", "native_source_tombstone_v1", "session_durable_send_v1", "daemon_installation_identity_v1", "session_user_boundary_v1", "session_terminal_outbox_v1"];
13
13
  export declare const SESSION_SYNC_EVENT_NAMES: readonly ["session.index.updated", "session.body.delta"];
14
14
  export declare const SESSION_HISTORY_LIMITS: {
15
15
  readonly defaultSnapshotMessages: 100;
@@ -201,6 +201,16 @@ export type DaemonSessionIndexProjection = {
201
201
  workDir: string;
202
202
  lastMessagePreview: string | null;
203
203
  bodyStorageMode: SessionBodyStorageMode;
204
+ /**
205
+ * Set only after the daemon has durably committed the first canonical user
206
+ * message. A pre-user agent/tool projection is never publishable.
207
+ */
208
+ localReady: true;
209
+ firstUserBoundary: {
210
+ role: 'user';
211
+ messageId: string;
212
+ bodyRevision: number;
213
+ };
204
214
  bodyRevision: number;
205
215
  indexRevision: number;
206
216
  firstAvailableSeq: number | null;
@@ -2,7 +2,7 @@
2
2
  // @test src/__tests__/session-sync.test.ts
3
3
  export const SESSION_SYNC_PROTOCOL_VERSION = 1;
4
4
  export const SESSION_SYNC_RELEASE_GATE = {
5
- minCliVersion: '0.3.42',
5
+ minCliVersion: '0.3.52',
6
6
  minAppBuild: { ios: 51, android: 12 },
7
7
  };
8
8
  export const SESSION_SYNC_REQUIRED_CAPABILITIES = [
@@ -17,6 +17,8 @@ export const SESSION_SYNC_REQUIRED_CAPABILITIES = [
17
17
  'native_source_tombstone_v1',
18
18
  'session_durable_send_v1',
19
19
  'daemon_installation_identity_v1',
20
+ 'session_user_boundary_v1',
21
+ 'session_terminal_outbox_v1',
20
22
  ];
21
23
  export const SESSION_SYNC_EVENT_NAMES = [
22
24
  'session.index.updated',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shennian",
3
- "version": "0.3.51",
3
+ "version": "0.3.52",
4
4
  "description": "Shennian — AI Agent Control Plane CLI",
5
5
  "type": "module",
6
6
  "bin": {