shennian 0.3.50 → 0.3.51
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/publish-build-manifest.json +4 -4
- package/dist/src/session/index-publisher.d.ts +2 -2
- package/dist/src/session/index-publisher.js +1 -1
- package/dist/src/session/store.d.ts +18 -1
- package/dist/src/session/store.js +2 -2
- package/node_modules/@shennian/wire/dist/methods.d.ts +1 -1
- package/node_modules/@shennian/wire/dist/session-sync.d.ts +23 -1
- package/node_modules/@shennian/wire/dist/session-sync.js +1 -0
- package/package.json +1 -1
|
@@ -576,8 +576,8 @@
|
|
|
576
576
|
},
|
|
577
577
|
{
|
|
578
578
|
"file": "src/session/index-publisher.js",
|
|
579
|
-
"beforeBytes":
|
|
580
|
-
"afterBytes":
|
|
579
|
+
"beforeBytes": 3011,
|
|
580
|
+
"afterBytes": 1330
|
|
581
581
|
},
|
|
582
582
|
{
|
|
583
583
|
"file": "src/session/manager.js",
|
|
@@ -596,8 +596,8 @@
|
|
|
596
596
|
},
|
|
597
597
|
{
|
|
598
598
|
"file": "src/session/store.js",
|
|
599
|
-
"beforeBytes":
|
|
600
|
-
"afterBytes":
|
|
599
|
+
"beforeBytes": 16551,
|
|
600
|
+
"afterBytes": 8868
|
|
601
601
|
},
|
|
602
602
|
{
|
|
603
603
|
"file": "src/session/tool-detail-store.js",
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { CliRelayClient } from '../relay/client.js';
|
|
2
|
-
export declare function publishPendingSessionIndexes(client: Pick<CliRelayClient, 'sendBufferedEvent'>): Promise<void>;
|
|
3
|
-
export declare function requestSessionIndexPublish(client: Pick<CliRelayClient, 'sendBufferedEvent'>): void;
|
|
2
|
+
export declare function publishPendingSessionIndexes(client: Pick<CliRelayClient, 'sendBufferedEvent' | 'sendReq'>): Promise<void>;
|
|
3
|
+
export declare function requestSessionIndexPublish(client: Pick<CliRelayClient, 'sendBufferedEvent' | 'sendReq'>): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{acknowledgeSessionIndexUpdate as
|
|
1
|
+
import a from"node:crypto";import{acknowledgeSessionIndexUpdate as c,applyGeneratedSessionTitle as f,claimSessionTitleGenerationCandidates as l,listPendingSessionIndexUpdates as p}from"./store.js";const i=new WeakMap,r=new WeakSet;function u(n){const s=n,o=i.get(s);if(o)return r.add(s),o;const e=g(n).finally(()=>{i.get(s)===e&&i.delete(s),r.delete(s)&&y(n)});return i.set(s,e),e}async function g(n){const s=p(),o=[];for(const e of s)try{await n.sendBufferedEvent({type:"event",event:"session.index.updated",id:`session-index-${e.daemonInstallationId}-${e.session.id}-${e.session.indexRevision}`,payload:e},6e4),c(e.session.id,e.session.indexRevision)}catch(t){o.push(t)}if(o.length>0)throw new AggregateError(o,"one or more session indexes failed");for(const e of l())try{const t=await n.sendReq({type:"req",id:`session-title-generate-${a.randomUUID()}`,method:"session.title.generate",params:e},15e3);if(!t.ok)continue;const d=t.payload;d?.generated&&f(e.sessionId,d)&&r.add(n)}catch(t){console.warn(`[session-title] generation attempt ended sessionId=${e.sessionId} requestId=${e.requestId}: ${t instanceof Error?t.message:String(t)}`)}}function y(n){u(n).catch(s=>{console.error("[session-index] sync failed; dirty revisions retained for retry",s)})}export{u as publishPendingSessionIndexes,y as requestSessionIndexPublish};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type AgentType, type Envelope, type ManagerConfig, type SessionIndexUpdate, type SessionDeliveryState, type SessionMessageStatusResponse, type SessionMode, type SessionTitleSource } from '@shennian/wire';
|
|
1
|
+
import { type AgentType, type Envelope, type ManagerConfig, type SessionIndexUpdate, type SessionTitleGenerateResponse, type SessionTitleGenerationEligibility, type SessionDeliveryState, type SessionMessageStatusResponse, type SessionMode, type SessionTitleSource } from '@shennian/wire';
|
|
2
2
|
import { PERSONAL_SYNC_V1_ROOT_NAME } from '../personal-sync/paths.js';
|
|
3
3
|
import { CanonicalSessionStore } from './canonical-store.js';
|
|
4
4
|
import { getDaemonInstallationId } from '../personal-sync/identity.js';
|
|
@@ -33,6 +33,8 @@ export type LocalSessionRecord = {
|
|
|
33
33
|
deletionRevision: number;
|
|
34
34
|
deletedAt: string | null;
|
|
35
35
|
syncedIndexRevision: number;
|
|
36
|
+
/** Local attempt marker only. The ephemeral titleSeed is never stored. */
|
|
37
|
+
titleGenerationAttemptedAt: string | null;
|
|
36
38
|
};
|
|
37
39
|
export { getDaemonInstallationId };
|
|
38
40
|
export declare function recordSession(input: {
|
|
@@ -60,6 +62,21 @@ export declare function purgeSessionRecord(sessionId: string): void;
|
|
|
60
62
|
export declare function purgeCanonicalSession(sessionId: string): void;
|
|
61
63
|
export declare function listPendingSessionIndexUpdates(): SessionIndexUpdate[];
|
|
62
64
|
export declare function acknowledgeSessionIndexUpdate(sessionId: string, indexRevision: number): void;
|
|
65
|
+
export type SessionTitleGenerationCandidate = {
|
|
66
|
+
sessionId: string;
|
|
67
|
+
requestId: string;
|
|
68
|
+
titleSeed: string;
|
|
69
|
+
baseTitleRevision: number;
|
|
70
|
+
eligibility: SessionTitleGenerationEligibility;
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Claims each fallback at most once. Only the durable attempt timestamp is
|
|
74
|
+
* written; the bounded seed returned to the caller never enters local JSON.
|
|
75
|
+
*/
|
|
76
|
+
export declare function claimSessionTitleGenerationCandidates(nowMs?: number, graceMs?: number): SessionTitleGenerationCandidate[];
|
|
77
|
+
export declare function applyGeneratedSessionTitle(sessionId: string, result: Extract<SessionTitleGenerateResponse, {
|
|
78
|
+
generated: true;
|
|
79
|
+
}>): boolean;
|
|
63
80
|
export declare function appendMessage(sessionId: string, envelope: Envelope, deliveryState?: SessionDeliveryState): number;
|
|
64
81
|
export declare function upsertMessage(sessionId: string, envelope: Envelope, deliveryState?: SessionDeliveryState): number;
|
|
65
82
|
export declare function getMessageDeliveryStatus(sessionId: string, clientMessageId: string): SessionMessageStatusResponse;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
`,"utf8"),
|
|
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 +1 @@
|
|
|
1
|
-
export type ReqMethod = 'chat.send' | 'chat.enqueue' | 'chat.queue.get' | 'chat.queue.edit' | 'chat.queue.delete' | 'chat.abort' | 'session.resume' | 'session.history' | 'session.history.open' | 'session.history.page' | 'session.history.close' | 'session.index.update' | 'session.deletions.sync' | 'session.deletions.ack' | 'session.message.status' | 'voice.session.body.window' | 'session.refresh' | 'session.tool.detail' | 'session.title.set' | 'fs.ls' | 'fs.read' | 'fs.write' | 'fs.export.markdown-pdf' | 'fs.export.markdown-pdf.setup' | 'fs.archive.zip' | 'fs.transfer.start' | 'fs.transfer.chunk' | 'fs.transfer.finish' | 'fs.transfer.abort' | 'fs.rename' | 'skill.list' | 'skill.install' | 'skill.doctor' | 'skill.setup' | 'skill.use' | 'region.probe' | 'region.switch' | 'upgrade.start' | 'upgrade.status' | 'upgrade.set-policy' | 'agents.refresh' | 'models.refresh' | 'agent.config.get' | 'agent.config.upsert' | 'agent.config.clear' | 'agent.config.test' | 'agent.capabilities.list' | 'manager.channel.get' | 'manager.channel.upsert' | 'session.wechat-rpa.channel.get' | 'session.wechat-rpa.channel.upsert' | 'session.wechat-rpa.channel.sync' | 'session.wechat-rpa.outbound.cancel' | 'manager.wechat-rpa.channel.get' | 'manager.wechat-rpa.channel.upsert' | 'manager.wechat-rpa.channel.sync' | 'manager.wechat-rpa.outbound.cancel' | 'wechat-rpa.helper.runtime' | 'wecom.send' | 'external.send' | 'room.sync' | 'room.send' | 'room.bind' | 'room.agent.bind-managed' | 'room.agent.restart-managed' | 'room.unbind';
|
|
1
|
+
export type ReqMethod = 'chat.send' | 'chat.enqueue' | 'chat.queue.get' | 'chat.queue.edit' | 'chat.queue.delete' | 'chat.abort' | 'session.resume' | 'session.history' | 'session.history.open' | 'session.history.page' | 'session.history.close' | 'session.index.update' | 'session.deletions.sync' | 'session.deletions.ack' | 'session.message.status' | 'voice.session.body.window' | 'session.refresh' | 'session.tool.detail' | 'session.title.set' | 'session.title.generate' | 'fs.ls' | 'fs.read' | 'fs.write' | 'fs.export.markdown-pdf' | 'fs.export.markdown-pdf.setup' | 'fs.archive.zip' | 'fs.transfer.start' | 'fs.transfer.chunk' | 'fs.transfer.finish' | 'fs.transfer.abort' | 'fs.rename' | 'skill.list' | 'skill.install' | 'skill.doctor' | 'skill.setup' | 'skill.use' | 'region.probe' | 'region.switch' | 'upgrade.start' | 'upgrade.status' | 'upgrade.set-policy' | 'agents.refresh' | 'models.refresh' | 'agent.config.get' | 'agent.config.upsert' | 'agent.config.clear' | 'agent.config.test' | 'agent.capabilities.list' | 'manager.channel.get' | 'manager.channel.upsert' | 'session.wechat-rpa.channel.get' | 'session.wechat-rpa.channel.upsert' | 'session.wechat-rpa.channel.sync' | 'session.wechat-rpa.outbound.cancel' | 'manager.wechat-rpa.channel.get' | 'manager.wechat-rpa.channel.upsert' | 'manager.wechat-rpa.channel.sync' | 'manager.wechat-rpa.outbound.cancel' | 'wechat-rpa.helper.runtime' | 'wecom.send' | 'external.send' | 'room.sync' | 'room.send' | 'room.bind' | 'room.agent.bind-managed' | 'room.agent.restart-managed' | 'room.unbind';
|
|
@@ -9,7 +9,7 @@ export declare const SESSION_SYNC_RELEASE_GATE: {
|
|
|
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_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"];
|
|
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;
|
|
@@ -37,6 +37,28 @@ export type SessionTitleSetParams = {
|
|
|
37
37
|
titleRevision: number;
|
|
38
38
|
titleOriginEventId: string;
|
|
39
39
|
};
|
|
40
|
+
export type SessionTitleGenerationEligibility = 'agent_native_unavailable' | 'terminal' | 'grace_elapsed';
|
|
41
|
+
/**
|
|
42
|
+
* Ephemeral daemon -> Server request. titleSeed must never be persisted or
|
|
43
|
+
* placed in an at-least-once event buffer by either side.
|
|
44
|
+
*/
|
|
45
|
+
export type SessionTitleGenerateParams = {
|
|
46
|
+
sessionId: string;
|
|
47
|
+
requestId: string;
|
|
48
|
+
titleSeed: string;
|
|
49
|
+
baseTitleRevision: number;
|
|
50
|
+
eligibility: SessionTitleGenerationEligibility;
|
|
51
|
+
};
|
|
52
|
+
export type SessionTitleGenerateResponse = {
|
|
53
|
+
generated: true;
|
|
54
|
+
title: string;
|
|
55
|
+
titleSource: 'shennian_generated';
|
|
56
|
+
titleRevision: number;
|
|
57
|
+
titleOriginEventId: string;
|
|
58
|
+
} | {
|
|
59
|
+
generated: false;
|
|
60
|
+
reason: 'not_eligible' | 'superseded' | 'generator_unavailable' | 'generation_failed';
|
|
61
|
+
};
|
|
40
62
|
export type SessionSyncErrorCode = 'upgrade_required' | 'revision_gap' | 'snapshot_compacted' | 'machine_offline' | 'history_unavailable' | 'subscription_not_found' | 'subscription_limit_exceeded' | 'response_too_large';
|
|
41
63
|
export type SessionSyncError = {
|
|
42
64
|
code: SessionSyncErrorCode;
|
|
@@ -11,6 +11,7 @@ export const SESSION_SYNC_REQUIRED_CAPABILITIES = [
|
|
|
11
11
|
'session_history_subscription_v1',
|
|
12
12
|
'local_canonical_v1',
|
|
13
13
|
'session_title_authority_v1',
|
|
14
|
+
'session_title_seed_v1',
|
|
14
15
|
'session_tool_detail_v1',
|
|
15
16
|
'voice_session_body_relay_v1',
|
|
16
17
|
'native_source_tombstone_v1',
|