wogiflow 2.30.4 → 2.31.1
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/.claude/commands/wogi-self-adversary.md +130 -0
- package/.claude/docs/config-schema.md +219 -0
- package/package.json +2 -2
- package/scripts/flow-defer-auth.js +41 -10
- package/scripts/flow-deferral-classifier-ai.js +3 -1
- package/scripts/flow-impl-question-classifier.js +178 -0
- package/scripts/flow-self-adversary-loop.js +422 -0
- package/scripts/flow-standards-gate.js +3 -1
- package/scripts/hooks/core/deferral-classifier.js +3 -0
- package/scripts/hooks/core/deferral-gate.js +6 -3
- package/scripts/hooks/core/gate-orchestrator.js +26 -1
- package/scripts/hooks/core/pre-tool-deps.js +11 -0
- package/scripts/hooks/core/pre-tool-orchestrator.js +21 -0
- package/scripts/hooks/core/self-adversary-gate.js +295 -0
- package/scripts/hooks/core/session-start-orchestrator.js +269 -0
- package/scripts/hooks/core/stop-orchestrator.js +123 -0
- package/scripts/hooks/core/task-boundary-restart-coordinator.js +84 -0
- package/scripts/hooks/core/user-prompt-orchestrator.js +201 -0
- package/scripts/hooks/core/workspace-stop-gates.js +133 -0
- package/scripts/hooks/core/workspace-stop-notify.js +76 -0
- package/scripts/hooks/entry/claude-code/session-start.js +19 -352
- package/scripts/hooks/entry/claude-code/stop.js +10 -485
- package/scripts/hooks/entry/claude-code/user-prompt-submit.js +9 -277
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Wogi Flow - Claude Code SessionStart Hook
|
|
4
|
+
* Wogi Flow - Claude Code SessionStart Hook (thin entry)
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* All SessionStart business logic lives in
|
|
7
|
+
* scripts/hooks/core/session-start-orchestrator.js. This entry dispatches.
|
|
8
|
+
*
|
|
9
|
+
* Per .claude/rules/architecture/hook-three-layer.md: entry files ≤ 120 LOC,
|
|
10
|
+
* ≤ 2 core/ imports, no inline business logic. wf-6e31850e A-3 extracted
|
|
11
|
+
* the prior 387-LOC body into core/session-start-orchestrator.js.
|
|
12
|
+
*
|
|
13
|
+
* Boot-latency instrumentation (env-guarded, no effect unless WOGI_DEBUG_BOOT=1)
|
|
14
|
+
* stays here because it wraps the call.
|
|
8
15
|
*/
|
|
9
16
|
|
|
10
|
-
const {
|
|
11
|
-
const { setCliSessionId, clearStaleCurrentTaskAsync, resetSessionTaskCounter } = require('../../../flow-session-state');
|
|
12
|
-
const { checkAndResetStalePhase } = require('../../core/phase-gate');
|
|
13
|
-
const { setRoutingPending } = require('../../core/routing-gate');
|
|
14
|
-
const { getConfig } = require('../../../flow-utils');
|
|
17
|
+
const { orchestrateSessionStart } = require('../../core/session-start-orchestrator');
|
|
15
18
|
const { runHook } = require('../shared/hook-runner');
|
|
16
19
|
|
|
17
|
-
// wf-8294d960: env-guarded boot-latency instrumentation.
|
|
18
|
-
// Claude Code suppresses hook process stderr — we write to an append-only log file instead.
|
|
20
|
+
// wf-8294d960: env-guarded boot-latency instrumentation.
|
|
19
21
|
const BOOT_DEBUG = process.env.WOGI_DEBUG_BOOT === '1';
|
|
20
22
|
const _bootT0 = BOOT_DEBUG ? Date.now() : 0;
|
|
21
23
|
const _bootLogFile = BOOT_DEBUG
|
|
@@ -34,354 +36,19 @@ function _bootWrite(line) {
|
|
|
34
36
|
}
|
|
35
37
|
function _bootMark(label) {
|
|
36
38
|
if (!BOOT_DEBUG) return;
|
|
37
|
-
|
|
38
|
-
_bootWrite(`[boot-latency] +${String(ms).padStart(6)}ms ${label}`);
|
|
39
|
+
_bootWrite(`[boot-latency] +${String(Date.now() - _bootT0).padStart(6)}ms ${label}`);
|
|
39
40
|
}
|
|
40
41
|
async function _bootTime(label, fn) {
|
|
41
42
|
if (!BOOT_DEBUG) return fn();
|
|
42
43
|
const t = Date.now();
|
|
43
|
-
try {
|
|
44
|
-
|
|
45
|
-
} finally {
|
|
46
|
-
_bootWrite(`[boot-latency] (${String(Date.now() - t).padStart(6)}ms) ${label}`);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Lazy-load bridge state to avoid circular dependencies
|
|
51
|
-
let autoSyncBridge = null;
|
|
52
|
-
function getAutoSyncBridge() {
|
|
53
|
-
if (!autoSyncBridge) {
|
|
54
|
-
try {
|
|
55
|
-
autoSyncBridge = require('../../../flow-bridge-state').autoSyncBridge;
|
|
56
|
-
} catch (_err) {
|
|
57
|
-
autoSyncBridge = async () => ({ synced: false, reason: 'unavailable' });
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
return autoSyncBridge;
|
|
44
|
+
try { return await fn(); }
|
|
45
|
+
finally { _bootWrite(`[boot-latency] (${String(Date.now() - t).padStart(6)}ms) ${label}`); }
|
|
61
46
|
}
|
|
62
47
|
|
|
63
48
|
runHook('SessionStart', async ({ parsedInput }) => {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const syncFn = getAutoSyncBridge();
|
|
69
|
-
await syncFn('claude-code', { silent: true });
|
|
70
|
-
} catch (err) {
|
|
71
|
-
if (process.env.DEBUG) {
|
|
72
|
-
console.error(`[session-start] Bridge auto-sync failed: ${err.message}`);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
49
|
+
return await orchestrateSessionStart({
|
|
50
|
+
parsedInput,
|
|
51
|
+
bootMark: _bootMark,
|
|
52
|
+
bootTime: _bootTime
|
|
75
53
|
});
|
|
76
|
-
|
|
77
|
-
// Wait for bridge sync to complete
|
|
78
|
-
await bridgeSyncPromise;
|
|
79
|
-
_bootMark('after bridge sync');
|
|
80
|
-
|
|
81
|
-
// wf-b8839d99: Refresh standing no-defer pin from decisions.md if a policy
|
|
82
|
-
// section is present. Fail-open — never blocks session start.
|
|
83
|
-
try {
|
|
84
|
-
const { refreshFromPolicy } = require('../../core/no-defer-policy');
|
|
85
|
-
const r = refreshFromPolicy();
|
|
86
|
-
if (r.refreshed && process.env.DEBUG) {
|
|
87
|
-
console.error(`[session-start] Refreshed no-defer pin from policy: ${r.header}`);
|
|
88
|
-
}
|
|
89
|
-
} catch (err) {
|
|
90
|
-
if (process.env.DEBUG) {
|
|
91
|
-
console.error(`[session-start] no-defer policy refresh failed: ${err.message}`);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// CLAUDE.md drift detection — check if manually edited since last sync
|
|
96
|
-
let driftDetected = false;
|
|
97
|
-
let driftMarkerMissing = false;
|
|
98
|
-
try {
|
|
99
|
-
const { checkClaudeMdDrift } = require('../../../flow-bridge-state');
|
|
100
|
-
const drift = checkClaudeMdDrift();
|
|
101
|
-
if (drift.drifted && drift.reason === 'content-changed') {
|
|
102
|
-
if (process.env.DEBUG) {
|
|
103
|
-
console.error('[session-start] CLAUDE.md drift detected — content changed since last sync');
|
|
104
|
-
}
|
|
105
|
-
driftDetected = true;
|
|
106
|
-
} else if (drift.drifted && drift.reason === 'marker-missing') {
|
|
107
|
-
if (process.env.DEBUG) {
|
|
108
|
-
console.error('[session-start] CLAUDE.md appears manually maintained (no generation marker)');
|
|
109
|
-
}
|
|
110
|
-
driftDetected = true;
|
|
111
|
-
driftMarkerMissing = true;
|
|
112
|
-
}
|
|
113
|
-
} catch (err) {
|
|
114
|
-
if (process.env.DEBUG) {
|
|
115
|
-
console.error(`[session-start] Drift detection failed: ${err.message}`);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// --- Version compatibility checks (parallelized) ---
|
|
120
|
-
let versionWarning = null;
|
|
121
|
-
let updateWarning = null;
|
|
122
|
-
await _bootTime('version checks', async () => {
|
|
123
|
-
try {
|
|
124
|
-
const { checkClaudeCodeVersionOnce, checkWogiFlowUpdateOnce } = require('../../../flow-version-check');
|
|
125
|
-
const [vw, uw] = await Promise.all([
|
|
126
|
-
(async () => { try { return await checkClaudeCodeVersionOnce(); } catch (_err) { return null; } })(),
|
|
127
|
-
(async () => { try { return await checkWogiFlowUpdateOnce(); } catch (_err) { return null; } })()
|
|
128
|
-
]);
|
|
129
|
-
versionWarning = vw;
|
|
130
|
-
updateWarning = uw;
|
|
131
|
-
} catch (err) {
|
|
132
|
-
if (process.env.DEBUG) {
|
|
133
|
-
console.error(`[session-start] Version check failed: ${err.message}`);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
});
|
|
137
|
-
_bootMark('after version checks');
|
|
138
|
-
|
|
139
|
-
// --- Batch 1: Independent pre-context operations (async + sync) ---
|
|
140
|
-
let scriptWarnings = [];
|
|
141
|
-
try {
|
|
142
|
-
const wasReset = checkAndResetStalePhase();
|
|
143
|
-
if (wasReset && process.env.DEBUG) {
|
|
144
|
-
console.error('[session-start] Reset stale workflow phase to idle');
|
|
145
|
-
}
|
|
146
|
-
} catch (err) {
|
|
147
|
-
if (process.env.DEBUG) {
|
|
148
|
-
console.error(`[session-start] Failed to check stale phase: ${err.message}`);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// Reset session task counter so first task uses full prompt
|
|
153
|
-
try {
|
|
154
|
-
resetSessionTaskCounter();
|
|
155
|
-
} catch (_err) {
|
|
156
|
-
// Non-blocking
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
try {
|
|
160
|
-
const routingResult = setRoutingPending();
|
|
161
|
-
if (process.env.DEBUG) {
|
|
162
|
-
console.error(`[session-start] Set routing-pending: ${routingResult.reason}`);
|
|
163
|
-
}
|
|
164
|
-
} catch (err) {
|
|
165
|
-
if (process.env.DEBUG) {
|
|
166
|
-
console.error(`[session-start] Failed to set routing-pending: ${err.message}`);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
try {
|
|
171
|
-
const { validateScripts } = require('../../../flow-script-resolver');
|
|
172
|
-
scriptWarnings = validateScripts();
|
|
173
|
-
} catch (err) {
|
|
174
|
-
if (process.env.DEBUG) {
|
|
175
|
-
console.error(`[session-start] Script validation failed: ${err.message}`);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// BUG-005 fix: Create durable-session.json for active tasks on session start.
|
|
180
|
-
try {
|
|
181
|
-
const { getReadyData } = require('../../../flow-utils');
|
|
182
|
-
const readyData = getReadyData();
|
|
183
|
-
if (Array.isArray(readyData.inProgress) && readyData.inProgress.length > 0) {
|
|
184
|
-
const task = readyData.inProgress[0];
|
|
185
|
-
const taskId = task && task.id;
|
|
186
|
-
if (taskId) {
|
|
187
|
-
const { loadDurableSession, createDurableSession } = require('../../../flow-durable-session');
|
|
188
|
-
const existing = loadDurableSession();
|
|
189
|
-
if (!existing || existing.taskId !== taskId) {
|
|
190
|
-
const criteria = task.acceptanceCriteria || task.scenarios || [];
|
|
191
|
-
const steps = Array.isArray(criteria) ? criteria : [];
|
|
192
|
-
const sessionSteps = steps.length > 0 ? steps : [task.title || taskId];
|
|
193
|
-
createDurableSession(taskId, 'task', sessionSteps);
|
|
194
|
-
if (process.env.DEBUG) {
|
|
195
|
-
console.error(`[session-start] Created durable session for active task ${taskId}`);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
} catch (err) {
|
|
201
|
-
if (process.env.DEBUG) {
|
|
202
|
-
console.error(`[session-start] Durable session init failed: ${err.message}`);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// Async operations — batch with Promise.all
|
|
207
|
-
const asyncPreOps = [];
|
|
208
|
-
|
|
209
|
-
if (parsedInput.sessionId) {
|
|
210
|
-
asyncPreOps.push(
|
|
211
|
-
setCliSessionId(parsedInput.sessionId).catch(err => {
|
|
212
|
-
if (process.env.DEBUG) {
|
|
213
|
-
console.error(`[session-start] Failed to store session ID: ${err.message}`);
|
|
214
|
-
}
|
|
215
|
-
})
|
|
216
|
-
);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
asyncPreOps.push(
|
|
220
|
-
clearStaleCurrentTaskAsync().catch(err => {
|
|
221
|
-
if (process.env.DEBUG) {
|
|
222
|
-
console.error(`[session-start] Failed to clear stale task: ${err.message}`);
|
|
223
|
-
}
|
|
224
|
-
})
|
|
225
|
-
);
|
|
226
|
-
|
|
227
|
-
// Gather session context concurrently with the async pre-ops
|
|
228
|
-
_bootMark('before gatherSessionContext + asyncPreOps');
|
|
229
|
-
const [, coreResult] = await Promise.all([
|
|
230
|
-
Promise.all(asyncPreOps),
|
|
231
|
-
_bootTime('gatherSessionContext', () => gatherSessionContext({
|
|
232
|
-
includeSuspended: true,
|
|
233
|
-
includeDecisions: true,
|
|
234
|
-
includeActivity: true
|
|
235
|
-
}))
|
|
236
|
-
]);
|
|
237
|
-
_bootMark('after gatherSessionContext + asyncPreOps');
|
|
238
|
-
|
|
239
|
-
// --- Batch 2: Post-context operations (plugin scan + community pull) ---
|
|
240
|
-
const postContextOps = [];
|
|
241
|
-
|
|
242
|
-
// Plugin auto-scan (non-blocking)
|
|
243
|
-
postContextOps.push((async () => {
|
|
244
|
-
try {
|
|
245
|
-
const config = getConfig();
|
|
246
|
-
if (config.plugins?.enabled && config.plugins?.autoScanOnSessionStart) {
|
|
247
|
-
const { scanUnregisteredMcpServers, registerPlugin, deactivateStaleMcpPlugins, listPlugins } = require('../../../flow-plugin-registry');
|
|
248
|
-
|
|
249
|
-
const unregistered = scanUnregisteredMcpServers();
|
|
250
|
-
for (const server of unregistered) {
|
|
251
|
-
registerPlugin({
|
|
252
|
-
name: server.serverName,
|
|
253
|
-
description: `Auto-discovered MCP server: ${server.serverName}`,
|
|
254
|
-
source: 'auto-scan',
|
|
255
|
-
triggers: [`use ${server.serverName}`, `send to ${server.serverName}`, server.serverName],
|
|
256
|
-
capabilities: [],
|
|
257
|
-
metadata: { mcpServer: server.serverName }
|
|
258
|
-
});
|
|
259
|
-
if (process.env.DEBUG) {
|
|
260
|
-
console.error(`[session-start] Auto-registered plugin: ${server.serverName}`);
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
const deactivated = deactivateStaleMcpPlugins();
|
|
265
|
-
if (deactivated.length > 0 && process.env.DEBUG) {
|
|
266
|
-
console.error(`[session-start] Deactivated ${deactivated.length} stale plugin(s): ${deactivated.join(', ')}`);
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
if (coreResult && coreResult.context) {
|
|
270
|
-
const activePlugins = listPlugins({ activeOnly: true });
|
|
271
|
-
if (unregistered.length > 0 || activePlugins.length > 0) {
|
|
272
|
-
coreResult.context.pluginScan = {
|
|
273
|
-
newlyRegistered: unregistered.map(s => s.serverName),
|
|
274
|
-
activePlugins: activePlugins.map(p => ({ name: p.name, capabilities: (p.capabilities || []).length }))
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
} catch (err) {
|
|
280
|
-
if (process.env.DEBUG) {
|
|
281
|
-
console.error(`[session-start] Plugin auto-scan failed: ${err.message}`);
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
})());
|
|
285
|
-
|
|
286
|
-
// Community knowledge pull + suggestion retry (non-blocking)
|
|
287
|
-
postContextOps.push((async () => {
|
|
288
|
-
try {
|
|
289
|
-
const communityConfig = getConfig();
|
|
290
|
-
if (communityConfig.community?.enabled) {
|
|
291
|
-
const community = require('../../../flow-community');
|
|
292
|
-
|
|
293
|
-
community.retryPendingSuggestions(communityConfig).catch(() => {});
|
|
294
|
-
|
|
295
|
-
if (communityConfig.community?.pullOnSessionStart !== false) {
|
|
296
|
-
const knowledge = await community.pullFromServer(communityConfig);
|
|
297
|
-
if (knowledge && coreResult && coreResult.context) {
|
|
298
|
-
coreResult.context.communityKnowledge = knowledge;
|
|
299
|
-
|
|
300
|
-
try {
|
|
301
|
-
community.mergeCommunityKnowledge(knowledge, communityConfig);
|
|
302
|
-
} catch (err) {
|
|
303
|
-
if (process.env.DEBUG) {
|
|
304
|
-
console.error(`[session-start] Community merge failed: ${err.message}`);
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
} catch (err) {
|
|
311
|
-
if (process.env.DEBUG) {
|
|
312
|
-
console.error(`[session-start] Community pull failed: ${err.message}`);
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
})());
|
|
316
|
-
|
|
317
|
-
await _bootTime('postContextOps (plugin-scan + community-pull)', () => Promise.all(postContextOps));
|
|
318
|
-
_bootMark('after postContextOps');
|
|
319
|
-
|
|
320
|
-
// Inject script warnings into context (if any)
|
|
321
|
-
if (scriptWarnings.length > 0 && coreResult && coreResult.context) {
|
|
322
|
-
coreResult.context.scriptWarnings = scriptWarnings.map(w => w.message);
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
// Inject version compatibility warning (if any)
|
|
326
|
-
if (versionWarning && coreResult && coreResult.context) {
|
|
327
|
-
coreResult.context.versionWarning = versionWarning;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
// Inject WogiFlow update warning (if any)
|
|
331
|
-
if (updateWarning && coreResult && coreResult.context) {
|
|
332
|
-
coreResult.context.updateWarning = updateWarning;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
// Inject drift detection results (if any)
|
|
336
|
-
if (driftDetected && coreResult && coreResult.context) {
|
|
337
|
-
if (driftMarkerMissing) {
|
|
338
|
-
coreResult.context.driftWarning = 'CLAUDE.md appears to have been manually edited (generation marker missing). Was this intentional? If yes, WogiFlow will respect your custom CLAUDE.md. If not, run `flow bridge sync` to regenerate from template.';
|
|
339
|
-
} else {
|
|
340
|
-
coreResult.context.driftWarning = 'CLAUDE.md content has changed since the last bridge sync. Was this intentional? If yes, WogiFlow will preserve your changes. If not, run `flow bridge sync` to regenerate from template.';
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
// State file drift detection (Claude Code 2.1.105+ — also works on older versions)
|
|
345
|
-
// Detects when .workflow/state/ files were modified externally between sessions
|
|
346
|
-
try {
|
|
347
|
-
const { detectDrift, saveSnapshot, formatDriftReport } = require('../../../flow-state-drift-detector');
|
|
348
|
-
const driftResult = detectDrift();
|
|
349
|
-
if (driftResult.hasDrift && coreResult && coreResult.context) {
|
|
350
|
-
coreResult.context.stateDriftWarning = formatDriftReport(driftResult);
|
|
351
|
-
}
|
|
352
|
-
// Always save a fresh snapshot at session start for next comparison
|
|
353
|
-
saveSnapshot();
|
|
354
|
-
} catch (_err) {
|
|
355
|
-
// State drift detection failure is non-fatal
|
|
356
|
-
if (process.env.DEBUG) {
|
|
357
|
-
console.error(`[session-start] State drift detection failed: ${_err.message}`);
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
// Workspace worker restart-handoff (wf-restart-handoff / 2.22.2).
|
|
362
|
-
// When the wogi-claude wrapper restarts a worker (via task-boundary-reset),
|
|
363
|
-
// queued dispatches from the PRIOR session are picked up by auto-resume;
|
|
364
|
-
// if the queue is truly empty, announce worker-ready so the manager can
|
|
365
|
-
// reconcile against its dispatch log and re-dispatch anything lost during
|
|
366
|
-
// the restart window. See scripts/hooks/core/session-start-worker.js.
|
|
367
|
-
await _bootTime('worker session-start handler', async () => {
|
|
368
|
-
try {
|
|
369
|
-
const { handleWorkerSessionStart } = require('../../core/session-start-worker');
|
|
370
|
-
const workerResult = handleWorkerSessionStart();
|
|
371
|
-
if (workerResult.context && coreResult && coreResult.context) {
|
|
372
|
-
if (workerResult.branch === 'auto-resume') {
|
|
373
|
-
coreResult.context.workerAutoResume = workerResult.context;
|
|
374
|
-
} else if (workerResult.branch === 'announce-ready') {
|
|
375
|
-
coreResult.context.workerReadyAnnounce = workerResult.context;
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
} catch (err) {
|
|
379
|
-
if (process.env.DEBUG) {
|
|
380
|
-
console.error(`[session-start] Worker session-start handler failed: ${err.message}`);
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
});
|
|
384
|
-
_bootMark('SessionStart hook returning');
|
|
385
|
-
|
|
386
|
-
return coreResult;
|
|
387
54
|
}, { failMode: 'warn' });
|