dsh-logicprobe 0.5.5 → 0.5.6

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/lib/index.js CHANGED
@@ -71,8 +71,19 @@ function gateMessage(text) {
71
71
  source: { kind: 'plugin', plugin: GATE_PLUGIN_ID },
72
72
  });
73
73
  }
74
+ function readSessionEvents(session) {
75
+ const source = session;
76
+ if (typeof source.snapshotEvents === 'function') {
77
+ const snapshot = source.snapshotEvents();
78
+ if (Array.isArray(snapshot))
79
+ return snapshot;
80
+ }
81
+ if (Array.isArray(source.events))
82
+ return source.events;
83
+ return [];
84
+ }
74
85
  function lastApprovalPolicy(session) {
75
- const events = session.events;
86
+ const events = readSessionEvents(session);
76
87
  for (let index = events.length - 1; index >= 0; index -= 1) {
77
88
  const event = events[index];
78
89
  if (event.type === 'approval/policy') {
@@ -82,7 +93,7 @@ function lastApprovalPolicy(session) {
82
93
  return undefined;
83
94
  }
84
95
  function planModeActive(session) {
85
- const events = session.events;
96
+ const events = readSessionEvents(session);
86
97
  for (let index = events.length - 1; index >= 0; index -= 1) {
87
98
  const event = events[index];
88
99
  if (event.type === 'plan/mode')
@@ -282,10 +293,10 @@ export function apply(ctx, config) {
282
293
  * first step that runs.
283
294
  */
284
295
  function gateInHistory(session) {
285
- return session.events.some((event) => {
296
+ return readSessionEvents(session).some((event) => {
286
297
  if (event.type !== 'user/message')
287
298
  return false;
288
- const source = event.data.source;
289
- return source.kind === 'plugin' && source.plugin === GATE_PLUGIN_ID;
299
+ const source = event.data?.source;
300
+ return source?.kind === 'plugin' && source.plugin === GATE_PLUGIN_ID;
290
301
  });
291
302
  }
package/lib/tool.js CHANGED
@@ -1,57 +1,57 @@
1
- import { defineTool } from '@deepseek-ai/dsh-tools';
2
- import { runVerification } from './engine.js';
3
- export const LOGICPROBE_VERIFY_TOOL_NAME = 'logicprobe_verify';
4
- /**
5
- * Model-visible DSH tool wrapping the bundled TypeScript verification engine.
6
- * The model passes a LogicModelV1 object; the engine validates it and returns
7
- * the 14-check report, or an 18-check report when beforeModel is supplied
8
- * (D1 behavioral preservation, D2 invariant continuity, D3 regression delta,
9
- * D4 deadlock/liveness regression). This is the dsh-native replacement for
10
- * hand-filling the Python template shipped in the skill references.
11
- */
12
- export const logicProbeVerifyTool = defineTool({
13
- name: LOGICPROBE_VERIFY_TOOL_NAME,
14
- description: 'Run executable state-machine verification (logicprobe). Takes a LogicModelV1 object with schemaVersion=1, init, states ({id, terminal?}), transitions ({from, event, to, guard?, updates?}), variables?, invariants?, concurrentPairs?, boundaryChecks?, resourcePairs?, idempotentEvents?, narrative?. Guards are structured ({variable, op, value} | {all} | {any} | {not}); invariants support never-states, var-in-range, event-before-state, leads-to, sequence, and atomicity; variables support monotonic inc/dec. The optional narrative block carries natural-language descriptions of states (narrative.states), events (narrative.events), and (state, event) scenarios (narrative.scenarios: [{from, event, scenario}]); when present it must fully cover the model and is echoed in the report. Returns a report with S1-S8 structural checks and A1-A11 adversarial probes including shortest counterexample paths. If beforeModel is provided, also runs D1-D4 before/after regression checks. See skills/logicprobe/references/dsh-model-schema.md.',
15
- parameters: {
16
- model: {
17
- type: 'json',
18
- required: true,
19
- description: 'LogicModelV1 state machine model to verify.',
20
- },
21
- maxStates: {
22
- type: 'integer',
23
- description: 'Maximum runtime states to explore. Default 10000.',
24
- },
25
- maxPermutationEvents: {
26
- type: 'integer',
27
- description: 'Maximum event count for A3 order permutation. Default 5.',
28
- },
29
- beforeModel: {
30
- type: 'json',
31
- description: 'Optional BEFORE LogicModelV1 state machine model for refactoring/migration regression comparison.',
32
- },
33
- stateMapping: {
34
- type: 'json',
35
- description: 'Optional object mapping BEFORE state ids to AFTER state ids. Omit for identity mapping (same state names).',
36
- },
37
- },
38
- output: {
39
- schema: {
40
- type: 'json',
41
- description: 'logicprobe verification report with summary and per-check findings.',
42
- },
43
- render(_args, value) {
44
- return [{ type: 'text', text: JSON.stringify(value, null, 2) }];
45
- },
46
- },
47
- timeoutMs: 10_000,
48
- isConcurrencySafe: () => true,
49
- async execute(args) {
50
- return runVerification(args.model, {
51
- maxStates: args.maxStates,
52
- maxPermutationEvents: args.maxPermutationEvents,
53
- beforeModel: args.beforeModel,
54
- stateMapping: args.stateMapping,
55
- });
56
- },
57
- });
1
+ import { defineTool } from '@deepseek-ai/dsh-tools';
2
+ import { runVerification } from './engine.js';
3
+ export const LOGICPROBE_VERIFY_TOOL_NAME = 'logicprobe_verify';
4
+ /**
5
+ * Model-visible DSH tool wrapping the bundled TypeScript verification engine.
6
+ * The model passes a LogicModelV1 object; the engine validates it and returns
7
+ * the 14-check report, or an 18-check report when beforeModel is supplied
8
+ * (D1 behavioral preservation, D2 invariant continuity, D3 regression delta,
9
+ * D4 deadlock/liveness regression). This is the dsh-native replacement for
10
+ * hand-filling the Python template shipped in the skill references.
11
+ */
12
+ export const logicProbeVerifyTool = defineTool({
13
+ name: LOGICPROBE_VERIFY_TOOL_NAME,
14
+ description: 'Run executable state-machine verification (logicprobe). Takes a LogicModelV1 object with schemaVersion=1, init, states ({id, terminal?}), transitions ({from, event, to, guard?, updates?}), variables?, invariants?, concurrentPairs?, boundaryChecks?, resourcePairs?, idempotentEvents?, narrative?. Guards are structured ({variable, op, value} | {all} | {any} | {not}); invariants support never-states, var-in-range, event-before-state, leads-to, sequence, and atomicity; variables support monotonic inc/dec. The optional narrative block carries natural-language descriptions of states (narrative.states), events (narrative.events), and (state, event) scenarios (narrative.scenarios: [{from, event, scenario}]); when present it must fully cover the model and is echoed in the report. Returns a report with S1-S8 structural checks and A1-A11 adversarial probes including shortest counterexample paths. If beforeModel is provided, also runs D1-D4 before/after regression checks. See skills/logicprobe/references/dsh-model-schema.md.',
15
+ parameters: {
16
+ model: {
17
+ type: 'json',
18
+ required: true,
19
+ description: 'LogicModelV1 state machine model to verify.',
20
+ },
21
+ maxStates: {
22
+ type: 'integer',
23
+ description: 'Maximum runtime states to explore. Default 10000.',
24
+ },
25
+ maxPermutationEvents: {
26
+ type: 'integer',
27
+ description: 'Maximum event count for A3 order permutation. Default 5.',
28
+ },
29
+ beforeModel: {
30
+ type: 'json',
31
+ description: 'Optional BEFORE LogicModelV1 state machine model for refactoring/migration regression comparison.',
32
+ },
33
+ stateMapping: {
34
+ type: 'json',
35
+ description: 'Optional object mapping BEFORE state ids to AFTER state ids. Omit for identity mapping (same state names).',
36
+ },
37
+ },
38
+ output: {
39
+ schema: {
40
+ type: 'json',
41
+ description: 'logicprobe verification report with summary and per-check findings.',
42
+ },
43
+ render(_args, value) {
44
+ return [{ type: 'text', text: JSON.stringify(value, null, 2) }];
45
+ },
46
+ },
47
+ timeoutMs: 10_000,
48
+ isConcurrencySafe: () => true,
49
+ async execute(args) {
50
+ return runVerification(args.model, {
51
+ maxStates: args.maxStates,
52
+ maxPermutationEvents: args.maxPermutationEvents,
53
+ beforeModel: args.beforeModel,
54
+ stateMapping: args.stateMapping,
55
+ });
56
+ },
57
+ });
@@ -1,189 +1,189 @@
1
- export declare const ENGINE_SCHEMA_VERSION = 1;
2
- export type VarValue = number | boolean;
3
- export type GuardOp = '==' | '!=' | '<' | '<=' | '>' | '>=';
4
- export interface LeafGuard {
5
- variable: string;
6
- op: GuardOp;
7
- value: VarValue;
8
- }
9
- export interface AllGuard {
10
- all: GuardNode[];
11
- }
12
- export interface AnyGuard {
13
- any: GuardNode[];
14
- }
15
- export interface NotGuard {
16
- not: GuardNode;
17
- }
18
- export type GuardNode = LeafGuard | AllGuard | AnyGuard | NotGuard;
19
- export interface StateSpec {
20
- id: string;
21
- terminal?: boolean;
22
- }
23
- export interface UpdateSpec {
24
- variable: string;
25
- op: 'set' | 'inc' | 'dec';
26
- value?: number;
27
- }
28
- export interface TransitionSpec {
29
- from: string;
30
- event: string;
31
- to: string;
32
- /** Absent guard is the else/default branch for the same (from, event) group. */
33
- guard?: GuardNode;
34
- updates?: UpdateSpec[];
35
- }
36
- export interface TransitionScenarioSpec {
37
- from: string;
38
- event: string;
39
- /** Natural language: what this (state, event) combination represents in the real scenario. */
40
- scenario: string;
41
- }
42
- export interface ModelNarrative {
43
- /** Natural-language meaning of each state id. */
44
- states?: Record<string, string>;
45
- /** Natural-language meaning of each event id. */
46
- events?: Record<string, string>;
47
- /** Natural-language scenario for each distinct (from, event) combination. */
48
- scenarios?: TransitionScenarioSpec[];
49
- }
50
- export interface VariableSpec {
51
- name: string;
52
- kind: 'integer' | 'boolean';
53
- init: number | boolean;
54
- min?: number;
55
- max?: number;
56
- monotonic?: 'inc' | 'dec';
57
- }
58
- export type InvariantSpec = {
59
- id: string;
60
- description: string;
61
- kind: 'never-states';
62
- states: string[];
63
- } | {
64
- id: string;
65
- description: string;
66
- kind: 'var-in-range';
67
- variable: string;
68
- min?: number;
69
- max?: number;
70
- } | {
71
- id: string;
72
- description: string;
73
- kind: 'event-before-state';
74
- event: string;
75
- state: string;
76
- } | {
77
- id: string;
78
- description: string;
79
- kind: 'leads-to';
80
- from: string;
81
- to: string;
82
- } | {
83
- id: string;
84
- description: string;
85
- kind: 'sequence';
86
- events: string[];
87
- } | {
88
- id: string;
89
- description: string;
90
- kind: 'atomicity';
91
- events: string[];
92
- commit: string;
93
- rollback?: string;
94
- };
95
- export interface ResourcePairSpec {
96
- resource: string;
97
- acquireEvent: string;
98
- releaseEvent: string;
99
- failEvent?: string;
100
- }
101
- export interface BoundaryCheckSpec {
102
- variable: string;
103
- values: number[];
104
- }
105
- export interface LogicModelV1 {
106
- schemaVersion: 1;
107
- init: string;
108
- states: StateSpec[];
109
- transitions: TransitionSpec[];
110
- variables?: VariableSpec[];
111
- invariants?: InvariantSpec[];
112
- concurrentPairs?: [string, string][];
113
- boundaryChecks?: BoundaryCheckSpec[];
114
- resourcePairs?: ResourcePairSpec[];
115
- idempotentEvents?: string[];
116
- /** Natural-language descriptions of states, events, and (state, event) scenarios. */
117
- narrative?: ModelNarrative;
118
- }
119
- export interface VerificationOptions {
120
- maxStates?: number;
121
- maxPermutationEvents?: number;
122
- beforeModel?: unknown;
123
- stateMapping?: Record<string, string>;
124
- }
125
- export interface PathStep {
126
- from: string;
127
- event: string;
128
- to: string;
129
- }
130
- export interface Finding {
131
- code: string;
132
- severity: 'error' | 'warning';
133
- message: string;
134
- path?: PathStep[];
135
- evidence?: Record<string, unknown>;
136
- }
137
- export interface CheckResult {
138
- id: string;
139
- name: string;
140
- status: 'pass' | 'fail' | 'skip';
141
- detail: string;
142
- findings: Finding[];
143
- }
144
- export interface VerificationReport {
145
- ok: boolean;
146
- schemaVersion: 1;
147
- modelHash: string;
148
- /** Echo of the model's natural-language narrative, when present. */
149
- narrative?: ModelNarrative;
150
- summary: {
151
- states: number;
152
- transitions: number;
153
- errors: number;
154
- warnings: number;
155
- checksRun: number;
156
- truncated?: boolean;
157
- };
158
- checks: CheckResult[];
159
- comparison?: ComparisonSummary;
160
- }
161
- export interface ComparisonSummary {
162
- beforeModelHash: string;
163
- afterModelHash: string;
164
- stateMapping: Record<string, string>;
165
- beforeStates: number;
166
- beforeTransitions: number;
167
- afterStates: number;
168
- afterTransitions: number;
169
- addedStates: string[];
170
- removedStates: string[];
171
- addedEvents: string[];
172
- removedEvents: string[];
173
- addedTransitions: TransitionSpec[];
174
- removedTransitions: TransitionSpec[];
175
- }
176
- export interface RuntimeState {
177
- state: string;
178
- vars: Record<string, VarValue>;
179
- }
180
- export declare function modelHash(model: LogicModelV1): string;
181
- export declare function validateModel(input: unknown): {
182
- ok: true;
183
- model: LogicModelV1;
184
- } | {
185
- ok: false;
186
- errors: string[];
187
- };
188
- export declare function guardVariables(guard: GuardNode | undefined): string[];
189
- export declare function runVerification(input: unknown, options?: VerificationOptions): VerificationReport;
1
+ export declare const ENGINE_SCHEMA_VERSION = 1;
2
+ export type VarValue = number | boolean;
3
+ export type GuardOp = '==' | '!=' | '<' | '<=' | '>' | '>=';
4
+ export interface LeafGuard {
5
+ variable: string;
6
+ op: GuardOp;
7
+ value: VarValue;
8
+ }
9
+ export interface AllGuard {
10
+ all: GuardNode[];
11
+ }
12
+ export interface AnyGuard {
13
+ any: GuardNode[];
14
+ }
15
+ export interface NotGuard {
16
+ not: GuardNode;
17
+ }
18
+ export type GuardNode = LeafGuard | AllGuard | AnyGuard | NotGuard;
19
+ export interface StateSpec {
20
+ id: string;
21
+ terminal?: boolean;
22
+ }
23
+ export interface UpdateSpec {
24
+ variable: string;
25
+ op: 'set' | 'inc' | 'dec';
26
+ value?: number;
27
+ }
28
+ export interface TransitionSpec {
29
+ from: string;
30
+ event: string;
31
+ to: string;
32
+ /** Absent guard is the else/default branch for the same (from, event) group. */
33
+ guard?: GuardNode;
34
+ updates?: UpdateSpec[];
35
+ }
36
+ export interface TransitionScenarioSpec {
37
+ from: string;
38
+ event: string;
39
+ /** Natural language: what this (state, event) combination represents in the real scenario. */
40
+ scenario: string;
41
+ }
42
+ export interface ModelNarrative {
43
+ /** Natural-language meaning of each state id. */
44
+ states?: Record<string, string>;
45
+ /** Natural-language meaning of each event id. */
46
+ events?: Record<string, string>;
47
+ /** Natural-language scenario for each distinct (from, event) combination. */
48
+ scenarios?: TransitionScenarioSpec[];
49
+ }
50
+ export interface VariableSpec {
51
+ name: string;
52
+ kind: 'integer' | 'boolean';
53
+ init: number | boolean;
54
+ min?: number;
55
+ max?: number;
56
+ monotonic?: 'inc' | 'dec';
57
+ }
58
+ export type InvariantSpec = {
59
+ id: string;
60
+ description: string;
61
+ kind: 'never-states';
62
+ states: string[];
63
+ } | {
64
+ id: string;
65
+ description: string;
66
+ kind: 'var-in-range';
67
+ variable: string;
68
+ min?: number;
69
+ max?: number;
70
+ } | {
71
+ id: string;
72
+ description: string;
73
+ kind: 'event-before-state';
74
+ event: string;
75
+ state: string;
76
+ } | {
77
+ id: string;
78
+ description: string;
79
+ kind: 'leads-to';
80
+ from: string;
81
+ to: string;
82
+ } | {
83
+ id: string;
84
+ description: string;
85
+ kind: 'sequence';
86
+ events: string[];
87
+ } | {
88
+ id: string;
89
+ description: string;
90
+ kind: 'atomicity';
91
+ events: string[];
92
+ commit: string;
93
+ rollback?: string;
94
+ };
95
+ export interface ResourcePairSpec {
96
+ resource: string;
97
+ acquireEvent: string;
98
+ releaseEvent: string;
99
+ failEvent?: string;
100
+ }
101
+ export interface BoundaryCheckSpec {
102
+ variable: string;
103
+ values: number[];
104
+ }
105
+ export interface LogicModelV1 {
106
+ schemaVersion: 1;
107
+ init: string;
108
+ states: StateSpec[];
109
+ transitions: TransitionSpec[];
110
+ variables?: VariableSpec[];
111
+ invariants?: InvariantSpec[];
112
+ concurrentPairs?: [string, string][];
113
+ boundaryChecks?: BoundaryCheckSpec[];
114
+ resourcePairs?: ResourcePairSpec[];
115
+ idempotentEvents?: string[];
116
+ /** Natural-language descriptions of states, events, and (state, event) scenarios. */
117
+ narrative?: ModelNarrative;
118
+ }
119
+ export interface VerificationOptions {
120
+ maxStates?: number;
121
+ maxPermutationEvents?: number;
122
+ beforeModel?: unknown;
123
+ stateMapping?: Record<string, string>;
124
+ }
125
+ export interface PathStep {
126
+ from: string;
127
+ event: string;
128
+ to: string;
129
+ }
130
+ export interface Finding {
131
+ code: string;
132
+ severity: 'error' | 'warning';
133
+ message: string;
134
+ path?: PathStep[];
135
+ evidence?: Record<string, unknown>;
136
+ }
137
+ export interface CheckResult {
138
+ id: string;
139
+ name: string;
140
+ status: 'pass' | 'fail' | 'skip';
141
+ detail: string;
142
+ findings: Finding[];
143
+ }
144
+ export interface VerificationReport {
145
+ ok: boolean;
146
+ schemaVersion: 1;
147
+ modelHash: string;
148
+ /** Echo of the model's natural-language narrative, when present. */
149
+ narrative?: ModelNarrative;
150
+ summary: {
151
+ states: number;
152
+ transitions: number;
153
+ errors: number;
154
+ warnings: number;
155
+ checksRun: number;
156
+ truncated?: boolean;
157
+ };
158
+ checks: CheckResult[];
159
+ comparison?: ComparisonSummary;
160
+ }
161
+ export interface ComparisonSummary {
162
+ beforeModelHash: string;
163
+ afterModelHash: string;
164
+ stateMapping: Record<string, string>;
165
+ beforeStates: number;
166
+ beforeTransitions: number;
167
+ afterStates: number;
168
+ afterTransitions: number;
169
+ addedStates: string[];
170
+ removedStates: string[];
171
+ addedEvents: string[];
172
+ removedEvents: string[];
173
+ addedTransitions: TransitionSpec[];
174
+ removedTransitions: TransitionSpec[];
175
+ }
176
+ export interface RuntimeState {
177
+ state: string;
178
+ vars: Record<string, VarValue>;
179
+ }
180
+ export declare function modelHash(model: LogicModelV1): string;
181
+ export declare function validateModel(input: unknown): {
182
+ ok: true;
183
+ model: LogicModelV1;
184
+ } | {
185
+ ok: false;
186
+ errors: string[];
187
+ };
188
+ export declare function guardVariables(guard: GuardNode | undefined): string[];
189
+ export declare function runVerification(input: unknown, options?: VerificationOptions): VerificationReport;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-logicprobe",
3
- "version": "0.5.5",
3
+ "version": "0.5.6",
4
4
  "description": "Design document & plan claim verification — enumerate claims, verify against codebase facts, then escalate to state-machine verification (S1-S8/A1-A11) and data-model verification (DS/DA/DD) for behavioral claims. Supports before/after regression, idempotency/monotonic/sequence/leads-to/atomicity constraints, and concurrency risk mining. Ships a native DeepSeek Harness (dsh) bundle that injects the claim-verification gate into the first model step.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -29,14 +29,15 @@
29
29
  "patch": "./cordis.patch.yml"
30
30
  },
31
31
  "compatibility": {
32
- "dsh": "^0.1.0-rc.7 || ^0.1.1-rc.1 || ^0.1.2-alpha.2 || ^0.1.2-alpha.3",
32
+ "dsh": "^0.1.0-rc.7 || ^0.1.1-rc.1 || ^0.1.2-alpha.2 || ^0.1.2-alpha.3 || ^0.1.2-alpha.4",
33
33
  "dshReleases": {
34
34
  "0.1.0-rc.7": "compatible",
35
35
  "0.1.0-rc.8": "compatible",
36
36
  "0.1.1-rc.1": "compatible",
37
37
  "0.1.1-rc.2": "compatible",
38
38
  "0.1.2-alpha.2": "compatible",
39
- "0.1.2-alpha.3": "compatible"
39
+ "0.1.2-alpha.3": "compatible",
40
+ "0.1.2-alpha.4": "compatible"
40
41
  },
41
42
  "profiles": [
42
43
  "headless"
package/src/index.ts CHANGED
@@ -97,8 +97,30 @@ interface SessionEventLike {
97
97
  data?: Record<string, unknown>
98
98
  }
99
99
 
100
+ /**
101
+ * Minimum session-store surface this plugin reads. DSH 0.1.2-alpha.4 replaced
102
+ * the `Session.events` getter with on-demand reads (`seq`, `eventAt()`,
103
+ * `snapshotEvents()`); releases up to 0.1.2-alpha.3 expose `events` as the
104
+ * full log snapshot. Read through a structural union so one build serves every
105
+ * declared DSH release.
106
+ */
107
+ interface SessionEventSource {
108
+ events?: readonly SessionEventLike[]
109
+ snapshotEvents?: () => readonly SessionEventLike[]
110
+ }
111
+
112
+ function readSessionEvents(session: Session): readonly SessionEventLike[] {
113
+ const source = session as unknown as SessionEventSource
114
+ if (typeof source.snapshotEvents === 'function') {
115
+ const snapshot = source.snapshotEvents()
116
+ if (Array.isArray(snapshot)) return snapshot
117
+ }
118
+ if (Array.isArray(source.events)) return source.events
119
+ return []
120
+ }
121
+
100
122
  function lastApprovalPolicy(session: Session): 'ask' | 'never' | undefined {
101
- const events = session.events as readonly SessionEventLike[]
123
+ const events = readSessionEvents(session)
102
124
  for (let index = events.length - 1; index >= 0; index -= 1) {
103
125
  const event = events[index]
104
126
  if (event.type === 'approval/policy') {
@@ -109,7 +131,7 @@ function lastApprovalPolicy(session: Session): 'ask' | 'never' | undefined {
109
131
  }
110
132
 
111
133
  function planModeActive(session: Session): boolean {
112
- const events = session.events as readonly SessionEventLike[]
134
+ const events = readSessionEvents(session)
113
135
  for (let index = events.length - 1; index >= 0; index -= 1) {
114
136
  const event = events[index]
115
137
  if (event.type === 'plan/mode') return event.data?.active === true
@@ -307,9 +329,9 @@ export function apply(ctx: Context, config: Config): void {
307
329
  * first step that runs.
308
330
  */
309
331
  function gateInHistory(session: Session): boolean {
310
- return session.events.some((event) => {
332
+ return readSessionEvents(session).some((event) => {
311
333
  if (event.type !== 'user/message') return false
312
- const source = event.data.source
313
- return source.kind === 'plugin' && source.plugin === GATE_PLUGIN_ID
334
+ const source = event.data?.source as { kind?: string; plugin?: string } | undefined
335
+ return source?.kind === 'plugin' && source.plugin === GATE_PLUGIN_ID
314
336
  })
315
337
  }