@deepseek-ai/dsh-goal 0.2.0-rc.2 → 0.2.1-alpha.2
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/README.i18n.yaml +4 -4
- package/README.md +1 -2
- package/README.zh.md +1 -2
- package/lib/typert.host.js +21 -5
- package/package.json +26 -33
- package/lib/invariant.js +0 -332
- package/lib/types/invariant.d.ts +0 -13
- package/lib/types/invariant.js +0 -71
package/README.i18n.yaml
CHANGED
|
@@ -42,14 +42,14 @@
|
|
|
42
42
|
en: 6f19e891ffc79754
|
|
43
43
|
zh: 9599e3b142f1e6b5
|
|
44
44
|
/deepseek-ai-dsh-goal/understand-the-implementation/source-map:
|
|
45
|
-
en:
|
|
46
|
-
zh:
|
|
45
|
+
en: aef62dff93f32796
|
|
46
|
+
zh: dba79da0466218a7
|
|
47
47
|
/deepseek-ai-dsh-goal/understand-the-implementation/events-and-attribution:
|
|
48
48
|
en: 502cf61dba8b22ca
|
|
49
49
|
zh: 2b6f672bb2ac84ba
|
|
50
50
|
/deepseek-ai-dsh-goal/further-exploration:
|
|
51
|
-
en:
|
|
52
|
-
zh:
|
|
51
|
+
en: 11e7ecd3115d0b6b
|
|
52
|
+
zh: d1dff8b7d05a2f09
|
|
53
53
|
/deepseek-ai-dsh-goal/model-experience:
|
|
54
54
|
en: 215e7ba838619b7b
|
|
55
55
|
zh: a311da3843709f90
|
package/README.md
CHANGED
|
@@ -109,7 +109,6 @@ This section explains how the service realizes the behavior above; the observabl
|
|
|
109
109
|
| [`src/types.ts`](src/types.ts) | Pure client-safe types: `GoalView`, `GoalSnapshot`, `GoalActivationChanged`, projection-key declaration |
|
|
110
110
|
| [`src/fold.ts`](src/fold.ts) | Strict replay fold and decoder for durable goal changes |
|
|
111
111
|
| [`src/runtime.ts`](src/runtime.ts) | `GoalId` brand, `GoalError` codes, change-version constant |
|
|
112
|
-
| [`src/invariant.ts`](src/invariant.ts) | Invariant companion: independent incremental fold over every attached session |
|
|
113
112
|
|
|
114
113
|
### Events and attribution
|
|
115
114
|
|
|
@@ -127,7 +126,7 @@ The package-level contract is enough for most consumers; read these when you nee
|
|
|
127
126
|
- [Goal subsystem](../../../docs/subsystems/goal.md) — the goal types, durable change payloads, and generated service API.
|
|
128
127
|
- [Goal group map](../README.md) — the goal packages and how they compose.
|
|
129
128
|
- [Generated configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-goal) — every accepted config field and its source declaration.
|
|
130
|
-
- [Goal domain Agent Note](../../../.agents/notes/
|
|
129
|
+
- [historical Goal domain Agent Note](../../../.agents/notes/archived/feature/2026-07-19-persisted-same-session-goal-domain.md) — the domain design, alternatives, and decisions.
|
|
131
130
|
|
|
132
131
|
-----
|
|
133
132
|
|
package/README.zh.md
CHANGED
|
@@ -109,7 +109,6 @@ view.activation // 'armed' | 'disarmed' — not persisted
|
|
|
109
109
|
| [`src/types.ts`](src/types.ts) | 纯客户端安全类型:`GoalView`、`GoalSnapshot`、`GoalActivationChanged`、投影键声明 |
|
|
110
110
|
| [`src/fold.ts`](src/fold.ts) | 持久 goal 变更的严格回放折叠与解码器 |
|
|
111
111
|
| [`src/runtime.ts`](src/runtime.ts) | `GoalId` 品牌、`GoalError` 代码、变更版本常量 |
|
|
112
|
-
| [`src/invariant.ts`](src/invariant.ts) | 不变式配套模块:对每个已挂接会话的独立增量折叠 |
|
|
113
112
|
|
|
114
113
|
### 事件与归属
|
|
115
114
|
|
|
@@ -127,7 +126,7 @@ view.activation // 'armed' | 'disarmed' — not persisted
|
|
|
127
126
|
- [goal 子系统](../../../docs/subsystems/goal.zh.md)——goal 类型、持久的变更载荷与生成的服务 API。
|
|
128
127
|
- [goal 组地图](../README.zh.md)——goal 各包及其组合方式。
|
|
129
128
|
- [生成的配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-goal)——每个受支持配置字段及其源声明。
|
|
130
|
-
- [goal 领域 Agent Note](../../../.agents/notes/
|
|
129
|
+
- [历史goal 领域 Agent Note](../../../.agents/notes/archived/feature/2026-07-19-persisted-same-session-goal-domain.md)——领域设计、备选方案与决策。
|
|
131
130
|
|
|
132
131
|
-----
|
|
133
132
|
|
package/lib/typert.host.js
CHANGED
|
@@ -775,16 +775,28 @@ export const TYPERT = {
|
|
|
775
775
|
},
|
|
776
776
|
{
|
|
777
777
|
"name": "LlmCallConfig",
|
|
778
|
-
"declaration": "export interface LlmCallConfig {\n provider: string;\n model: string;\n
|
|
778
|
+
"declaration": "export interface LlmCallConfig extends LlmCallControls {\n provider: string;\n model: string;\n}"
|
|
779
779
|
},
|
|
780
780
|
{
|
|
781
781
|
"name": "LlmCallConfigAdapterDefaults",
|
|
782
782
|
"declaration": "export interface LlmCallConfigAdapterDefaults {\n reasoningEffort?: true;\n maxTokens?: true;\n}"
|
|
783
783
|
},
|
|
784
|
+
{
|
|
785
|
+
"name": "LlmCallControls",
|
|
786
|
+
"declaration": "export interface LlmCallControls {\n reasoningEffort?: ReasoningEffortId;\n temperature?: number;\n maxTokens?: number;\n stop?: string[];\n}"
|
|
787
|
+
},
|
|
784
788
|
{
|
|
785
789
|
"name": "LlmFailure",
|
|
786
790
|
"declaration": "export interface LlmFailure {\n readonly message: string;\n readonly code: string;\n readonly status?: number;\n readonly providerRetryAfterMs?: number;\n readonly requestId?: ProviderRequestId;\n readonly offloadImages?: number;\n}"
|
|
787
791
|
},
|
|
792
|
+
{
|
|
793
|
+
"name": "LlmRetryEventData",
|
|
794
|
+
"declaration": "export type LlmRetryEventData = { retryId: RetryId; turn: number; step: number; provider: string; mode: 'normal'; policyKey: string; retry: number; maxRetries: number; delayMs: number; failure: LlmFailure; } | { retryId: RetryId; turn: number; step: number; provider: string; mode: 'always'; policyKey: string; retry: number; delayMs: number; failure: LlmFailure; };"
|
|
795
|
+
},
|
|
796
|
+
{
|
|
797
|
+
"name": "LlmRetryStartedEventData",
|
|
798
|
+
"declaration": "export interface LlmRetryStartedEventData {\n retryId: RetryId;\n turn: number;\n step: number;\n retry: number;\n}"
|
|
799
|
+
},
|
|
788
800
|
{
|
|
789
801
|
"name": "Message",
|
|
790
802
|
"declaration": "export type Message = MessageRoleMap[keyof MessageRoleMap];"
|
|
@@ -827,7 +839,7 @@ export const TYPERT = {
|
|
|
827
839
|
},
|
|
828
840
|
{
|
|
829
841
|
"name": "MessageSourceMap",
|
|
830
|
-
"declaration": "export interface MessageSourceMap {\n user: { kind: 'user'; };\n model: ModelMessageSource;\n tool: ToolMessageSource;\n 'system-prompt': SystemPromptMessageSource;\n 'model-selection': { kind: 'model-selection'; } & ContextFormed;\n 'user-approval': { kind: 'user-approval'; } & ContextFormed;\n 'ptc-mode': { kind: 'ptc-mode'; };\n 'tool-registry': { kind: 'tool-registry'; };\n 'user-rpc': { kind: 'user'; rpcId: SessionRequestId; clientTimeZone?: string; };\n 'agent-message': AgentMessageSource;\n 'subagent-settled': SubagentSettledMessageSource;\n 'skill-invocation': SkillInvocationSource;\n 'cordis-host-runner': { kind: 'cordis-host-runner'; };\n
|
|
842
|
+
"declaration": "export interface MessageSourceMap {\n user: { kind: 'user'; };\n model: ModelMessageSource;\n tool: ToolMessageSource;\n 'system-prompt': SystemPromptMessageSource;\n 'model-selection': { kind: 'model-selection'; } & ContextFormed;\n 'working-directory': { kind: 'working-directory'; };\n 'user-approval': { kind: 'user-approval'; } & ContextFormed;\n 'ptc-mode': { kind: 'ptc-mode'; };\n 'tool-registry': { kind: 'tool-registry'; };\n 'user-rpc': { kind: 'user'; rpcId: SessionRequestId; clientTimeZone?: string; };\n 'agent-message': AgentMessageSource;\n 'subagent-settled': SubagentSettledMessageSource;\n 'skill-invocation': SkillInvocationSource;\n 'cordis-host-runner': { kind: 'cordis-host-runner'; };\n 'compact-checkpoint': CompactionCheckpointSource;\n 'user-question-reply': { kind: 'user-question-reply'; callId: ToolCallId; outcome: 'answered'; };\n goal: GoalMessageSource;\n schedule: { kind: 'schedule'; } & ContextFormed;\n 'session-reference': SessionReferenceSource;\n}"
|
|
831
843
|
},
|
|
832
844
|
{
|
|
833
845
|
"name": "ModelMessageSource",
|
|
@@ -855,7 +867,7 @@ export const TYPERT = {
|
|
|
855
867
|
},
|
|
856
868
|
{
|
|
857
869
|
"name": "PtcDispatchEventData",
|
|
858
|
-
"declaration": "export interface PtcDispatchEventData extends PtcDispatchStartEventData {\n isError: boolean;\n content: ContentBlock[];\n error?: { name: string; code: string; reason?: string; };\n}"
|
|
870
|
+
"declaration": "export interface PtcDispatchEventData extends PtcDispatchStartEventData {\n isError: boolean;\n content: ContentBlock[];\n meta?: JsonValue;\n error?: { name: string; code: string; reason?: string; };\n}"
|
|
859
871
|
},
|
|
860
872
|
{
|
|
861
873
|
"name": "PtcDispatchStartEventData",
|
|
@@ -881,6 +893,10 @@ export const TYPERT = {
|
|
|
881
893
|
"name": "RequestHeaderReason",
|
|
882
894
|
"declaration": "export type RequestHeaderReason = 'initial' | 'resume' | 'change' | 'series';"
|
|
883
895
|
},
|
|
896
|
+
{
|
|
897
|
+
"name": "RetryId",
|
|
898
|
+
"declaration": "export type RetryId = Branded<'RetryId'>;"
|
|
899
|
+
},
|
|
884
900
|
{
|
|
885
901
|
"name": "SandboxMode",
|
|
886
902
|
"declaration": "export type SandboxMode = 'read-only' | 'workspace-write' | 'danger-full-access';"
|
|
@@ -915,7 +931,7 @@ export const TYPERT = {
|
|
|
915
931
|
},
|
|
916
932
|
{
|
|
917
933
|
"name": "SessionEventMap",
|
|
918
|
-
"declaration": "export interface SessionEventMap {\n 'turn/start': { turn: number; };\n 'turn/end': { turn: number; reason: TurnEndReason; };\n 'step/start': { turn: number; step: number; };\n 'step/end': { turn: number; step: number; };\n 'user/message': UserMessage;\n 'developer/message': { turn: number; step: number; message: DeveloperMessage; headerSeq?: SessionSeq; };\n 'system/message': { turn: number; step: number; message: SystemMessage; };\n 'assistant/message': { turn: number; step: number; message: AssistantMessage; stream: AssistantStreamRecord[]; usage?: TokenUsage; interrupted?: true; };\n 'assistant/attempt': { turn: number; step: number; stream: AssistantStreamRecord[]; };\n 'tool/call': { turn: number; step: number; callId: ToolCallId; name: string; arguments: string; };\n 'tool/result': { turn: number; step: number; message: ToolResultMessage; error?: { name: string; code: string; reason?: string; }; meta?: JsonValue; };\n 'request/header': { header: EpochHeader; reason: RequestHeaderReason; startsSeries?: true; };\n 'request/context': RequestContext;\n 'session/end-seed': { inherited?: true; };\n 'agent/inbox/spliced': { target: InboxTarget; start: number; removedCount?: number; inserted: UserMessage[]; outcome?: 'canceled'; };\n 'sandbox/mode': { mode: SandboxMode; source?: 'delegation'; };\n 'approval/asked': { id: ApprovalRequestId; toolName: string; callId?: ToolCallId; reason?: string; };\n 'approval/decided': { id: ApprovalRequestId; outcome: ApprovalOutcome; };\n 'approval/policy': { policy: ApprovalPolicy; source?: 'delegation'; };\n 'tool/ptc-dispatch-start': PtcDispatchStartEventData;\n 'tool/ptc-dispatch': PtcDispatchEventData;\n 'agent-preset/selected': { agentPreset: string; };\n 'command/run': { commandId: CommandId; name: string; args?: string; source: CommandSource; };\n 'command/done': { commandId: CommandId; kind: 'success' | 'error'; text?: string; sourceEventSeq?: import('@deepseek-ai/dsh-session/types').SessionSeq; };\n 'image/offload': { targets: ImageOffloadTarget[]; };\n 'session/title': SessionTitleEventData;\n 'todo/write': { todos: TodoItem[]; };\n 'model/selection': ModelSelection;\n '
|
|
934
|
+
"declaration": "export interface SessionEventMap {\n 'turn/start': { turn: number; };\n 'turn/end': { turn: number; reason: TurnEndReason; };\n 'step/start': { turn: number; step: number; };\n 'step/end': { turn: number; step: number; };\n 'user/message': UserMessage;\n 'developer/message': { turn: number; step: number; message: DeveloperMessage; headerSeq?: SessionSeq; };\n 'system/message': { turn: number; step: number; message: SystemMessage; };\n 'assistant/message': { turn: number; step: number; message: AssistantMessage; stream: AssistantStreamRecord[]; usage?: TokenUsage; interrupted?: true; };\n 'assistant/attempt': { turn: number; step: number; stream: AssistantStreamRecord[]; };\n 'tool/call': { turn: number; step: number; callId: ToolCallId; name: string; arguments: string; };\n 'tool/result': { turn: number; step: number; message: ToolResultMessage; error?: { name: string; code: string; reason?: string; }; meta?: JsonValue; };\n 'request/header': { header: EpochHeader; reason: RequestHeaderReason; startsSeries?: true; };\n 'request/context': RequestContext;\n 'session/end-seed': { inherited?: true; };\n 'agent/inbox/spliced': { target: InboxTarget; start: number; removedCount?: number; inserted: UserMessage[]; outcome?: 'canceled'; };\n 'working-directory/change': { cwd: string; };\n 'sandbox/mode': { mode: SandboxMode; source?: 'delegation'; };\n 'approval/asked': { id: ApprovalRequestId; toolName: string; callId?: ToolCallId; reason?: string; };\n 'approval/decided': { id: ApprovalRequestId; outcome: ApprovalOutcome; };\n 'approval/policy': { policy: ApprovalPolicy; source?: 'delegation'; };\n 'tool/ptc-dispatch-start': PtcDispatchStartEventData;\n 'tool/ptc-dispatch': PtcDispatchEventData;\n 'agent-preset/selected': { agentPreset: string; };\n 'command/run': { commandId: CommandId; name: string; args?: string; source: CommandSource; };\n 'command/done': { commandId: CommandId; kind: 'success' | 'error'; text?: string; sourceEventSeq?: import('@deepseek-ai/dsh-session/types').SessionSeq; };\n 'image/offload': { targets: ImageOffloadTarget[]; };\n 'session/title': SessionTitleEventData;\n 'todo/write': { todos: TodoItem[]; };\n 'model/selection': ModelSelection;\n 'permission/preset': { preset: string; };\n 'subagent/descriptor': SubagentDescriptorData;\n 'subagent/catalog': SubagentCatalogEvent;\n 'compaction/start': { compactionId: CompactionId; sourceCommandId?: CommandId; turn: number | null; };\n 'compaction/summary': { compactionId: CompactionId; sourceCommandId?: CommandId; summary: ContentBlock[]; shadowedRange: { start: SessionSeq; end: SessionSeq; }; shadowedSeqs: SessionSeq[]; shadowedTokenCount: number; provider: string; model: string; maxTokens?: number; usage?: TokenUsage; } & ({ rawOutput: ContentBlock[]; llmStreamCall: true; } | { rawOutput?: ContentBlock[]; llmStreamCall?: never; });\n 'compaction/end': { compactionId: CompactionId; sourceCommandId?: CommandId; turn: number | null; error?: string; };\n 'compaction/prune': { shadowedRange: { start: SessionSeq; end: SessionSeq; }; shadowedSeqs: SessionSeq[]; shadowedTokenCount: number; };\n 'feedback/record': FeedbackRecord;\n 'llm/retry': LlmRetryEventData;\n 'llm/retry-started': LlmRetryStartedEventData;\n 'goal/change': GoalChangeMeta;\n 'feedback/message-put': MessageFeedbackPut;\n 'feedback/message-delete': MessageFeedbackDelete;\n 'schedule/change': ScheduleChange;\n}"
|
|
919
935
|
},
|
|
920
936
|
{
|
|
921
937
|
"name": "SessionEventType",
|
|
@@ -975,7 +991,7 @@ export const TYPERT = {
|
|
|
975
991
|
},
|
|
976
992
|
{
|
|
977
993
|
"name": "SubagentCatalogEvent",
|
|
978
|
-
"declaration": "export type SubagentCatalogEvent = { readonly childId: SessionId; readonly childCreatedAt: number; } & (({ readonly version: 0; } & KnownCatalogMode) | ({ readonly version: 1; } & (KnownCatalogMode | { readonly mode: 'unknown'; readonly label?: string; })));"
|
|
994
|
+
"declaration": "export type SubagentCatalogEvent = { readonly childId: SessionId; readonly childCreatedAt: number; } & (({ readonly version: 0; } & KnownCatalogMode) | ({ readonly version: 1; } & (KnownCatalogMode | { readonly mode: 'unknown'; readonly label?: string; })) | { readonly version: 2; readonly mode: 'external'; readonly label?: string; });"
|
|
979
995
|
},
|
|
980
996
|
{
|
|
981
997
|
"name": "SubagentDescriptorBase",
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-goal",
|
|
3
3
|
"description": "Event-sourced same-session goal state and lifecycle service for the DeepSeek Harness",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.1-alpha.2",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -18,10 +18,6 @@
|
|
|
18
18
|
"types": "./lib/types/index.d.ts",
|
|
19
19
|
"default": "./lib/index.js"
|
|
20
20
|
},
|
|
21
|
-
"./invariant": {
|
|
22
|
-
"types": "./lib/types/invariant.d.ts",
|
|
23
|
-
"default": "./lib/invariant.js"
|
|
24
|
-
},
|
|
25
21
|
"./types": {
|
|
26
22
|
"types": "./lib/types/types.d.ts",
|
|
27
23
|
"default": "./lib/types/types.js"
|
|
@@ -43,7 +39,6 @@
|
|
|
43
39
|
},
|
|
44
40
|
"files": [
|
|
45
41
|
"lib/index.js",
|
|
46
|
-
"lib/invariant.js",
|
|
47
42
|
"lib/types/**/*.js",
|
|
48
43
|
"lib/types/**/*.d.ts",
|
|
49
44
|
"lib/typert.host.js",
|
|
@@ -53,36 +48,34 @@
|
|
|
53
48
|
],
|
|
54
49
|
"license": "MIT",
|
|
55
50
|
"peerDependencies": {
|
|
56
|
-
"@deepseek-ai/dsh-
|
|
57
|
-
"@deepseek-ai/dsh-
|
|
58
|
-
"@deepseek-ai/dsh-
|
|
59
|
-
"@deepseek-ai/dsh-
|
|
60
|
-
"@deepseek-ai/dsh-
|
|
61
|
-
"@deepseek-ai/dsh-session": "0.2.
|
|
62
|
-
"@deepseek-ai/dsh-typert-protocol": "0.2.
|
|
63
|
-
"@deepseek-ai/cordis": "~4.0.
|
|
64
|
-
"@deepseek-ai/dsh-scope": "0.2.0-rc.2"
|
|
51
|
+
"@deepseek-ai/dsh-agent": "0.2.1-alpha.2",
|
|
52
|
+
"@deepseek-ai/dsh-session-projection": "0.2.1-alpha.2",
|
|
53
|
+
"@deepseek-ai/dsh-brand": "0.2.1-alpha.2",
|
|
54
|
+
"@deepseek-ai/dsh-llm": "0.2.1-alpha.2",
|
|
55
|
+
"@deepseek-ai/dsh-scope": "0.2.1-alpha.2",
|
|
56
|
+
"@deepseek-ai/dsh-session": "0.2.1-alpha.2",
|
|
57
|
+
"@deepseek-ai/dsh-typert-protocol": "0.2.1-alpha.2",
|
|
58
|
+
"@deepseek-ai/cordis": "~4.0.5-alpha.1"
|
|
65
59
|
},
|
|
66
60
|
"dependencies": {
|
|
67
|
-
"
|
|
68
|
-
"
|
|
61
|
+
"@deepseek-ai/schemastery": "~3.18.5-alpha.1",
|
|
62
|
+
"zod": "^4.4.3"
|
|
69
63
|
},
|
|
70
64
|
"devDependencies": {
|
|
71
|
-
"@deepseek-ai/dsh-agent": "0.2.
|
|
72
|
-
"@deepseek-ai/dsh-
|
|
73
|
-
"@deepseek-ai/dsh-agent-loop": "0.2.
|
|
74
|
-
"@deepseek-ai/dsh-
|
|
75
|
-
"@deepseek-ai/dsh-session-projection": "0.2.
|
|
76
|
-
"@deepseek-ai/dsh-brand": "0.2.
|
|
77
|
-
"@deepseek-ai/dsh-
|
|
78
|
-
"@deepseek-ai/dsh-
|
|
79
|
-
"@deepseek-ai/dsh-
|
|
80
|
-
"@deepseek-ai/dsh-session
|
|
81
|
-
"@deepseek-ai/dsh-session-
|
|
82
|
-
"@deepseek-ai/dsh-
|
|
83
|
-
"@deepseek-ai/
|
|
84
|
-
"@deepseek-ai/dsh-
|
|
85
|
-
"@deepseek-ai/
|
|
86
|
-
"@deepseek-ai/dsh-typert-protocol": "0.2.0-rc.2"
|
|
65
|
+
"@deepseek-ai/dsh-agent": "0.2.1-alpha.2",
|
|
66
|
+
"@deepseek-ai/dsh-agent-loop": "0.2.1-alpha.2",
|
|
67
|
+
"@deepseek-ai/dsh-agent-loop-testkit": "0.2.1-alpha.2",
|
|
68
|
+
"@deepseek-ai/dsh-bash-local": "0.2.1-alpha.2",
|
|
69
|
+
"@deepseek-ai/dsh-session-projection": "0.2.1-alpha.2",
|
|
70
|
+
"@deepseek-ai/dsh-brand": "0.2.1-alpha.2",
|
|
71
|
+
"@deepseek-ai/dsh-llm": "0.2.1-alpha.2",
|
|
72
|
+
"@deepseek-ai/dsh-loader-smoke": "0.2.1-alpha.2",
|
|
73
|
+
"@deepseek-ai/dsh-scope": "0.2.1-alpha.2",
|
|
74
|
+
"@deepseek-ai/dsh-session": "0.2.1-alpha.2",
|
|
75
|
+
"@deepseek-ai/dsh-session-checkpoint-policy": "0.2.1-alpha.2",
|
|
76
|
+
"@deepseek-ai/dsh-session-persistence-jsonl": "0.2.1-alpha.2",
|
|
77
|
+
"@deepseek-ai/dsh-subprocess-local": "0.2.1-alpha.2",
|
|
78
|
+
"@deepseek-ai/dsh-typert-protocol": "0.2.1-alpha.2",
|
|
79
|
+
"@deepseek-ai/cordis": "~4.0.5-alpha.1"
|
|
87
80
|
}
|
|
88
81
|
}
|
package/lib/invariant.js
DELETED
|
@@ -1,332 +0,0 @@
|
|
|
1
|
-
import "@deepseek-ai/dsh-llm";
|
|
2
|
-
//#region lib/types/runtime.js
|
|
3
|
-
/**
|
|
4
|
-
* Brand a string as a goal id.
|
|
5
|
-
* @param id - raw goal identifier.
|
|
6
|
-
* @returns the same string with the compile-time brand.
|
|
7
|
-
*/
|
|
8
|
-
function GoalId(id) {
|
|
9
|
-
return id;
|
|
10
|
-
}
|
|
11
|
-
//#endregion
|
|
12
|
-
//#region lib/types/fold.js
|
|
13
|
-
/** Pure replay fold and strict decoder for durable goal changes. */
|
|
14
|
-
const SNAPSHOT_OPERATIONS = new Set([
|
|
15
|
-
"create",
|
|
16
|
-
"edit",
|
|
17
|
-
"pause",
|
|
18
|
-
"resume",
|
|
19
|
-
"complete",
|
|
20
|
-
"block"
|
|
21
|
-
]);
|
|
22
|
-
const PHASES = new Set([
|
|
23
|
-
"active",
|
|
24
|
-
"paused",
|
|
25
|
-
"blocked",
|
|
26
|
-
"complete"
|
|
27
|
-
]);
|
|
28
|
-
/**
|
|
29
|
-
* Build an empty replay accumulator.
|
|
30
|
-
* @returns mutable state with no current goal or prior ref.
|
|
31
|
-
*/
|
|
32
|
-
function emptyGoalFoldState() {
|
|
33
|
-
return {
|
|
34
|
-
goal: void 0,
|
|
35
|
-
roundsStarted: 0,
|
|
36
|
-
createdAt: void 0,
|
|
37
|
-
updatedAt: void 0,
|
|
38
|
-
lastRef: void 0,
|
|
39
|
-
seenGoalIds: /* @__PURE__ */ new Set()
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
/** Whether a value is a JSON record rather than an array. */
|
|
43
|
-
function isRecord(value) {
|
|
44
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
45
|
-
}
|
|
46
|
-
/** Require one positive safe integer. */
|
|
47
|
-
function positiveInteger(value, field) {
|
|
48
|
-
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1) throw new Error(`goal change ${field} must be a positive safe integer`);
|
|
49
|
-
return value;
|
|
50
|
-
}
|
|
51
|
-
/** Require one non-negative safe integer. */
|
|
52
|
-
function nonNegativeInteger(value, field) {
|
|
53
|
-
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) throw new Error(`goal change ${field} must be a non-negative safe integer`);
|
|
54
|
-
return value;
|
|
55
|
-
}
|
|
56
|
-
/** Decode one canonical blocker explanation. */
|
|
57
|
-
function decodeBlockReason(value) {
|
|
58
|
-
if (!isRecord(value) || Object.keys(value).sort().join(",") !== "code,message") throw new Error("goal change goal.blockedReason must have exactly code and message fields");
|
|
59
|
-
if (typeof value["code"] !== "string" || !/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/.test(value["code"])) throw new Error("goal change goal.blockedReason.code must be lower-kebab-case");
|
|
60
|
-
if (typeof value["message"] !== "string" || value["message"].trim().length === 0 || value["message"] !== value["message"].trim()) throw new Error("goal change goal.blockedReason.message must be non-empty and normalized");
|
|
61
|
-
return {
|
|
62
|
-
code: value["code"],
|
|
63
|
-
message: value["message"]
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
/** Decode and validate one snapshot. */
|
|
67
|
-
function decodeSnapshot(value) {
|
|
68
|
-
if (!isRecord(value)) throw new Error("goal change goal must be a record");
|
|
69
|
-
if (typeof value["id"] !== "string" || value["id"].length === 0) throw new Error("goal change goal.id must be a non-empty string");
|
|
70
|
-
if (typeof value["objective"] !== "string" || value["objective"].trim().length === 0 || value["objective"] !== value["objective"].trim()) throw new Error("goal change goal.objective must be non-empty and normalized");
|
|
71
|
-
if (typeof value["phase"] !== "string" || !PHASES.has(value["phase"])) throw new Error("goal change goal.phase is invalid");
|
|
72
|
-
const phase = value["phase"];
|
|
73
|
-
const expectedKeys = phase === "blocked" ? "blockedReason,id,maxGoalRounds,objective,phase,revision" : "id,maxGoalRounds,objective,phase,revision";
|
|
74
|
-
if (Object.keys(value).sort().join(",") !== expectedKeys) throw new Error(`goal change goal for phase ${phase} must have exactly ${expectedKeys} fields`);
|
|
75
|
-
return {
|
|
76
|
-
id: GoalId(value["id"]),
|
|
77
|
-
revision: positiveInteger(value["revision"], "goal.revision"),
|
|
78
|
-
objective: value["objective"],
|
|
79
|
-
phase,
|
|
80
|
-
maxGoalRounds: positiveInteger(value["maxGoalRounds"], "goal.maxGoalRounds"),
|
|
81
|
-
...phase === "blocked" ? { blockedReason: decodeBlockReason(value["blockedReason"]) } : {}
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
/** Decode and validate one ref. */
|
|
85
|
-
function decodeRef(value) {
|
|
86
|
-
if (!isRecord(value) || Object.keys(value).sort().join(",") !== "id,revision") throw new Error("goal clear tombstone must have exactly id and revision fields");
|
|
87
|
-
if (typeof value["id"] !== "string" || value["id"].length === 0) throw new Error("goal clear tombstone id must be a non-empty string");
|
|
88
|
-
return {
|
|
89
|
-
id: GoalId(value["id"]),
|
|
90
|
-
revision: positiveInteger(value["revision"], "cleared.revision")
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* Decode a value that declares itself as a goal change. Unrelated values
|
|
95
|
-
* return `undefined`; malformed goal changes fail replay loudly.
|
|
96
|
-
* @param value - candidate source change.
|
|
97
|
-
* @returns validated goal change or `undefined` for another value kind.
|
|
98
|
-
*/
|
|
99
|
-
function decodeGoalChange(value) {
|
|
100
|
-
if (!isRecord(value) || value["kind"] !== "goal/change") return void 0;
|
|
101
|
-
if (value["version"] !== 1) throw new Error(`unsupported goal change version ${String(value["version"])}`);
|
|
102
|
-
if (value["operation"] === "clear") {
|
|
103
|
-
const allowed = [
|
|
104
|
-
"cleared",
|
|
105
|
-
"clearedAt",
|
|
106
|
-
"kind",
|
|
107
|
-
"operation",
|
|
108
|
-
"version"
|
|
109
|
-
];
|
|
110
|
-
if (Object.keys(value).sort().join(",") !== allowed.sort().join(",")) throw new Error(`goal clear change must have exactly ${allowed.sort().join(",")} fields`);
|
|
111
|
-
return {
|
|
112
|
-
kind: "goal/change",
|
|
113
|
-
version: 1,
|
|
114
|
-
operation: "clear",
|
|
115
|
-
cleared: decodeRef(value["cleared"]),
|
|
116
|
-
clearedAt: nonNegativeInteger(value["clearedAt"], "clearedAt")
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
if (typeof value["operation"] !== "string" || !SNAPSHOT_OPERATIONS.has(value["operation"])) throw new Error("goal change operation is invalid");
|
|
120
|
-
const allowed = [
|
|
121
|
-
"createdAt",
|
|
122
|
-
"goal",
|
|
123
|
-
"kind",
|
|
124
|
-
"operation",
|
|
125
|
-
"roundsStarted",
|
|
126
|
-
"updatedAt",
|
|
127
|
-
"version"
|
|
128
|
-
];
|
|
129
|
-
if (Object.keys(value).sort().join(",") !== allowed.sort().join(",")) throw new Error(`goal snapshot change must have exactly ${allowed.sort().join(",")} fields`);
|
|
130
|
-
const createdAt = nonNegativeInteger(value["createdAt"], "createdAt");
|
|
131
|
-
const updatedAt = nonNegativeInteger(value["updatedAt"], "updatedAt");
|
|
132
|
-
if (updatedAt < createdAt) throw new Error("goal change updatedAt cannot precede createdAt");
|
|
133
|
-
return {
|
|
134
|
-
kind: "goal/change",
|
|
135
|
-
version: 1,
|
|
136
|
-
operation: value["operation"],
|
|
137
|
-
goal: decodeSnapshot(value["goal"]),
|
|
138
|
-
roundsStarted: nonNegativeInteger(value["roundsStarted"], "roundsStarted"),
|
|
139
|
-
createdAt,
|
|
140
|
-
updatedAt
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
/** Narrow model attribution to a valid goal source. */
|
|
144
|
-
function goalSource(source) {
|
|
145
|
-
if (source.kind !== "goal") return void 0;
|
|
146
|
-
if (typeof source.goalId !== "string" || source.goalId.length === 0 || !Number.isSafeInteger(source.revision) || source.revision < 1 || !Number.isSafeInteger(source.round) || source.round < 1) throw new Error("goal message source is invalid");
|
|
147
|
-
return source;
|
|
148
|
-
}
|
|
149
|
-
/** Require two snapshots to retain fields that only `edit` may replace. */
|
|
150
|
-
function requireSameDefinition(current, next, operation) {
|
|
151
|
-
if (next.objective !== current.objective || next.maxGoalRounds !== current.maxGoalRounds) throw new Error(`goal ${operation} cannot change objective or maxGoalRounds`);
|
|
152
|
-
}
|
|
153
|
-
/** Require one exact next revision of the current goal. */
|
|
154
|
-
function requireNextRevision(current, next, operation) {
|
|
155
|
-
if (next.id !== current.id || next.revision !== current.revision + 1) throw new Error(`goal ${operation} must advance the current goal by one revision`);
|
|
156
|
-
}
|
|
157
|
-
/** Validate one non-create snapshot operation against the preceding projection. */
|
|
158
|
-
function validateSnapshotTransition(state, change, current) {
|
|
159
|
-
const next = change.goal;
|
|
160
|
-
requireNextRevision(current, next, change.operation);
|
|
161
|
-
/* v8 ignore next -- a current goal established by this fold always has an updatedAt */
|
|
162
|
-
if (state.updatedAt === void 0) throw new Error("current goal fold lacks updatedAt");
|
|
163
|
-
if (change.createdAt !== state.createdAt || change.updatedAt < state.updatedAt || change.roundsStarted !== state.roundsStarted) throw new Error(`goal ${change.operation} does not preserve the current counters and timestamps`);
|
|
164
|
-
switch (change.operation) {
|
|
165
|
-
case "edit":
|
|
166
|
-
if (next.phase !== current.phase || JSON.stringify(next.blockedReason) !== JSON.stringify(current.blockedReason)) throw new Error("goal edit cannot change phase or blocked reason");
|
|
167
|
-
break;
|
|
168
|
-
case "pause":
|
|
169
|
-
requireSameDefinition(current, next, change.operation);
|
|
170
|
-
if (current.phase !== "active" || next.phase !== "paused") throw new Error("goal pause has an invalid phase transition");
|
|
171
|
-
break;
|
|
172
|
-
case "resume":
|
|
173
|
-
requireSameDefinition(current, next, change.operation);
|
|
174
|
-
if (!new Set([
|
|
175
|
-
"active",
|
|
176
|
-
"paused",
|
|
177
|
-
"blocked"
|
|
178
|
-
]).has(current.phase) || next.phase !== "active" || state.roundsStarted >= next.maxGoalRounds) throw new Error("goal resume has an invalid phase transition or exhausted round budget");
|
|
179
|
-
break;
|
|
180
|
-
case "complete":
|
|
181
|
-
requireSameDefinition(current, next, change.operation);
|
|
182
|
-
if (current.phase === "complete" || next.phase !== "complete") throw new Error("goal complete has an invalid phase transition");
|
|
183
|
-
break;
|
|
184
|
-
case "block":
|
|
185
|
-
requireSameDefinition(current, next, change.operation);
|
|
186
|
-
if (current.phase !== "active" || next.phase !== "blocked") throw new Error("goal block has an invalid phase transition");
|
|
187
|
-
break;
|
|
188
|
-
/* v8 ignore start -- the caller excludes create and GoalOperation is closed; these arms retain fail-loud exhaustiveness */
|
|
189
|
-
case "create": throw new Error("goal create cannot be validated as a current-goal transition");
|
|
190
|
-
default:
|
|
191
|
-
change.operation;
|
|
192
|
-
throw new Error("unknown goal snapshot operation");
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
/**
|
|
196
|
-
* Return the revision identity carried by a snapshot or tombstone.
|
|
197
|
-
* @param change - decoded goal mutation.
|
|
198
|
-
* @returns stable identity used to reconcile a deferred change with its log event.
|
|
199
|
-
*/
|
|
200
|
-
function goalChangeRef(change) {
|
|
201
|
-
return change.operation === "clear" ? change.cleared : {
|
|
202
|
-
id: change.goal.id,
|
|
203
|
-
revision: change.goal.revision
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
/**
|
|
207
|
-
* Validate and apply one decoded change to a mutable accumulator.
|
|
208
|
-
* @param state - preceding durable goal projection.
|
|
209
|
-
* @param change - decoded full snapshot or clear tombstone.
|
|
210
|
-
*/
|
|
211
|
-
function applyGoalChange(state, change) {
|
|
212
|
-
const ref = goalChangeRef(change);
|
|
213
|
-
if (change.operation === "clear") {
|
|
214
|
-
const current = state.goal;
|
|
215
|
-
if (current === void 0) throw new Error("goal clear requires a current goal");
|
|
216
|
-
requireNextRevision(current, change.cleared, change.operation);
|
|
217
|
-
/* v8 ignore next -- a current goal established by this fold always has an updatedAt */
|
|
218
|
-
if (state.updatedAt === void 0) throw new Error("current goal fold lacks updatedAt");
|
|
219
|
-
if (change.clearedAt < state.updatedAt) throw new Error("goal clear timestamp cannot precede the current goal update");
|
|
220
|
-
state.goal = void 0;
|
|
221
|
-
state.roundsStarted = 0;
|
|
222
|
-
state.createdAt = void 0;
|
|
223
|
-
state.updatedAt = void 0;
|
|
224
|
-
state.lastRef = ref;
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
|
-
if (change.operation === "create") {
|
|
228
|
-
if (change.goal.revision !== 1 || change.goal.phase !== "active" || change.roundsStarted !== 0 || state.goal !== void 0 && state.goal.phase !== "complete" || state.seenGoalIds.has(change.goal.id)) throw new Error("goal create requires a fresh active revision-one goal with zero rounds");
|
|
229
|
-
state.seenGoalIds.add(change.goal.id);
|
|
230
|
-
} else {
|
|
231
|
-
const current = state.goal;
|
|
232
|
-
if (current === void 0) throw new Error(`goal ${change.operation} requires a current goal`);
|
|
233
|
-
validateSnapshotTransition(state, change, current);
|
|
234
|
-
}
|
|
235
|
-
state.goal = change.goal;
|
|
236
|
-
state.roundsStarted = change.roundsStarted;
|
|
237
|
-
state.createdAt = change.createdAt;
|
|
238
|
-
state.updatedAt = change.updatedAt;
|
|
239
|
-
state.lastRef = ref;
|
|
240
|
-
}
|
|
241
|
-
/**
|
|
242
|
-
* Apply one session event to the strict durable goal fold.
|
|
243
|
-
* @param state - mutable fold accumulator.
|
|
244
|
-
* @param event - next event in sequence order.
|
|
245
|
-
*/
|
|
246
|
-
function applyGoalEvent(state, event) {
|
|
247
|
-
if (event.type === "goal/change") {
|
|
248
|
-
const change = decodeGoalChange(event.data);
|
|
249
|
-
/* v8 ignore next -- the event's declared payload always identifies itself as a goal change. */
|
|
250
|
-
if (change === void 0) throw new Error(`goal change at session event ${event.seq} has an invalid kind`);
|
|
251
|
-
applyGoalChange(state, change);
|
|
252
|
-
return;
|
|
253
|
-
}
|
|
254
|
-
if (event.type === "user/message") {
|
|
255
|
-
const source = goalSource(event.data.source);
|
|
256
|
-
if (source === void 0) return;
|
|
257
|
-
const current = state.goal;
|
|
258
|
-
if (current === void 0 || current.phase !== "active" || source.goalId !== current.id || source.revision !== current.revision || source.round !== state.roundsStarted + 1 || source.round > current.maxGoalRounds) throw new Error(`goal round at session event ${event.seq} is not the next admitted round of the active goal`);
|
|
259
|
-
state.roundsStarted = source.round;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
//#endregion
|
|
263
|
-
//#region lib/types/invariant.js
|
|
264
|
-
/** Package-owned durable goal-stream invariants. @module @deepseek-ai/dsh-goal/invariant */
|
|
265
|
-
const PACKAGE_NAME = "@deepseek-ai/dsh-goal";
|
|
266
|
-
/** Cordis companion plugin name. */
|
|
267
|
-
const name = "goal-invariant";
|
|
268
|
-
/** Service required before the companion can reserve package ownership. */
|
|
269
|
-
const inject = ["invariants"];
|
|
270
|
-
/** Copy the independent fold before validating one candidate event. */
|
|
271
|
-
function cloneState(state) {
|
|
272
|
-
return {
|
|
273
|
-
goal: state.goal,
|
|
274
|
-
roundsStarted: state.roundsStarted,
|
|
275
|
-
createdAt: state.createdAt,
|
|
276
|
-
updatedAt: state.updatedAt,
|
|
277
|
-
lastRef: state.lastRef,
|
|
278
|
-
seenGoalIds: new Set(state.seenGoalIds)
|
|
279
|
-
};
|
|
280
|
-
}
|
|
281
|
-
/** Apply one event through the strict goal decoder and attribute failures. */
|
|
282
|
-
function applyChecked(state, event, fail) {
|
|
283
|
-
try {
|
|
284
|
-
applyGoalEvent(state, event);
|
|
285
|
-
} catch (error) {
|
|
286
|
-
/* v8 ignore next -- the strict goal decoder throws Error instances */
|
|
287
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
288
|
-
fail(`session event ${event.seq} violates the durable goal stream: ${message}`);
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
/** Install an independent incremental fold over every attached session. */
|
|
292
|
-
const install = Object.assign((ctx, fail) => {
|
|
293
|
-
const states = /* @__PURE__ */ new WeakMap();
|
|
294
|
-
const staged = /* @__PURE__ */ new WeakMap();
|
|
295
|
-
const seed = (session) => {
|
|
296
|
-
const state = emptyGoalFoldState();
|
|
297
|
-
for (const event of session.snapshotEvents()) applyChecked(state, event, fail);
|
|
298
|
-
states.set(session, state);
|
|
299
|
-
return state;
|
|
300
|
-
};
|
|
301
|
-
/* v8 ignore next -- session/event always follows list() or session/created seeding */
|
|
302
|
-
const stateFor = (session) => states.get(session) ?? seed(session);
|
|
303
|
-
for (const session of ctx.sessions.list()) seed(session);
|
|
304
|
-
ctx.on("session/created", (session) => {
|
|
305
|
-
seed(session);
|
|
306
|
-
}, { global: true });
|
|
307
|
-
ctx.on("internal/dispatch", (_mode, eventName, args) => {
|
|
308
|
-
if (eventName !== "session/event") return;
|
|
309
|
-
const [session, event] = args;
|
|
310
|
-
const state = cloneState(stateFor(session));
|
|
311
|
-
applyChecked(state, event, fail);
|
|
312
|
-
staged.set(event, {
|
|
313
|
-
session,
|
|
314
|
-
state
|
|
315
|
-
});
|
|
316
|
-
}, { global: true });
|
|
317
|
-
ctx.on("session/event", (session, event) => {
|
|
318
|
-
const candidate = staged.get(event);
|
|
319
|
-
/* v8 ignore next 2 -- internal/dispatch stages the exact callback arguments */
|
|
320
|
-
if (candidate === void 0 || candidate.session !== session) return fail("session/event reached publication without matching goal-fold validation");
|
|
321
|
-
staged.delete(event);
|
|
322
|
-
states.set(session, candidate.state);
|
|
323
|
-
}, { global: true });
|
|
324
|
-
}, { inject: ["sessions"] });
|
|
325
|
-
/**
|
|
326
|
-
* Register the goal-stream invariant companion.
|
|
327
|
-
* @param ctx - Cordis context carrying the invariant service.
|
|
328
|
-
* @returns the installed registration's disposer after setup succeeds.
|
|
329
|
-
*/
|
|
330
|
-
const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
|
|
331
|
-
//#endregion
|
|
332
|
-
export { apply, inject, name };
|
package/lib/types/invariant.d.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/** Package-owned durable goal-stream invariants. @module @deepseek-ai/dsh-goal/invariant */
|
|
2
|
-
import type { Context } from '@deepseek-ai/cordis';
|
|
3
|
-
/** Cordis companion plugin name. */
|
|
4
|
-
export declare const name = "goal-invariant";
|
|
5
|
-
/** Service required before the companion can reserve package ownership. */
|
|
6
|
-
export declare const inject: string[];
|
|
7
|
-
/**
|
|
8
|
-
* Register the goal-stream invariant companion.
|
|
9
|
-
* @param ctx - Cordis context carrying the invariant service.
|
|
10
|
-
* @returns the installed registration's disposer after setup succeeds.
|
|
11
|
-
*/
|
|
12
|
-
export declare const apply: (ctx: Context) => Promise<() => void>;
|
|
13
|
-
//# sourceMappingURL=invariant.d.ts.map
|
package/lib/types/invariant.js
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
/** Package-owned durable goal-stream invariants. @module @deepseek-ai/dsh-goal/invariant */
|
|
2
|
-
import { applyGoalEvent, emptyGoalFoldState } from "./fold.js";
|
|
3
|
-
const PACKAGE_NAME = '@deepseek-ai/dsh-goal';
|
|
4
|
-
/** Cordis companion plugin name. */
|
|
5
|
-
export const name = 'goal-invariant';
|
|
6
|
-
/** Service required before the companion can reserve package ownership. */
|
|
7
|
-
export const inject = ['invariants'];
|
|
8
|
-
/** Copy the independent fold before validating one candidate event. */
|
|
9
|
-
function cloneState(state) {
|
|
10
|
-
return {
|
|
11
|
-
goal: state.goal,
|
|
12
|
-
roundsStarted: state.roundsStarted,
|
|
13
|
-
createdAt: state.createdAt,
|
|
14
|
-
updatedAt: state.updatedAt,
|
|
15
|
-
lastRef: state.lastRef,
|
|
16
|
-
seenGoalIds: new Set(state.seenGoalIds),
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
/** Apply one event through the strict goal decoder and attribute failures. */
|
|
20
|
-
function applyChecked(state, event, fail) {
|
|
21
|
-
try {
|
|
22
|
-
applyGoalEvent(state, event);
|
|
23
|
-
}
|
|
24
|
-
catch (error) {
|
|
25
|
-
/* v8 ignore next -- the strict goal decoder throws Error instances */
|
|
26
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
27
|
-
fail(`session event ${event.seq} violates the durable goal stream: ${message}`);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
/** Install an independent incremental fold over every attached session. */
|
|
31
|
-
const install = Object.assign((ctx, fail) => {
|
|
32
|
-
const states = new WeakMap();
|
|
33
|
-
const staged = new WeakMap();
|
|
34
|
-
const seed = (session) => {
|
|
35
|
-
const state = emptyGoalFoldState();
|
|
36
|
-
// oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
|
|
37
|
-
for (const event of session.snapshotEvents())
|
|
38
|
-
applyChecked(state, event, fail);
|
|
39
|
-
states.set(session, state);
|
|
40
|
-
return state;
|
|
41
|
-
};
|
|
42
|
-
/* v8 ignore next -- session/event always follows list() or session/created seeding */
|
|
43
|
-
const stateFor = (session) => states.get(session) ?? seed(session);
|
|
44
|
-
for (const session of ctx.sessions.list())
|
|
45
|
-
seed(session);
|
|
46
|
-
ctx.on('session/created', (session) => { seed(session); }, { global: true });
|
|
47
|
-
ctx.on('internal/dispatch', (_mode, eventName, args) => {
|
|
48
|
-
if (eventName !== 'session/event')
|
|
49
|
-
return;
|
|
50
|
-
const [session, event] = args;
|
|
51
|
-
const state = cloneState(stateFor(session));
|
|
52
|
-
applyChecked(state, event, fail);
|
|
53
|
-
staged.set(event, { session, state });
|
|
54
|
-
}, { global: true });
|
|
55
|
-
ctx.on('session/event', (session, event) => {
|
|
56
|
-
const candidate = staged.get(event);
|
|
57
|
-
/* v8 ignore next 2 -- internal/dispatch stages the exact callback arguments */
|
|
58
|
-
if (candidate === undefined || candidate.session !== session) {
|
|
59
|
-
return fail('session/event reached publication without matching goal-fold validation');
|
|
60
|
-
}
|
|
61
|
-
staged.delete(event);
|
|
62
|
-
states.set(session, candidate.state);
|
|
63
|
-
}, { global: true });
|
|
64
|
-
}, { inject: ['sessions'] });
|
|
65
|
-
/**
|
|
66
|
-
* Register the goal-stream invariant companion.
|
|
67
|
-
* @param ctx - Cordis context carrying the invariant service.
|
|
68
|
-
* @returns the installed registration's disposer after setup succeeds.
|
|
69
|
-
*/
|
|
70
|
-
export const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
|
|
71
|
-
//# sourceMappingURL=invariant.js.map
|