total-recall-brain 3.19.1 → 3.20.0
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "total-recall-brain",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.20.0",
|
|
4
4
|
"description": "Personal AI system — database-free, SSSS-powered intelligence engine deployable to any POSIX host.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"workspaces": [
|
|
@@ -79,8 +79,6 @@
|
|
|
79
79
|
"zod": "^4.4.3"
|
|
80
80
|
},
|
|
81
81
|
"devDependencies": {
|
|
82
|
-
"@rolldown/binding-darwin-x64": "^1.2.0",
|
|
83
|
-
"@rolldown/pluginutils": "^1.0.1",
|
|
84
82
|
"@testing-library/dom": "^10.4.1",
|
|
85
83
|
"@testing-library/jest-dom": "^6.9.1",
|
|
86
84
|
"@testing-library/react": "^16.3.2",
|
|
@@ -1,30 +1,66 @@
|
|
|
1
1
|
agents:
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
# Google Antigravity CLI. The binary is `agy` (NOT `antigravity`) and it runs
|
|
3
|
+
# on the AI Ultra plan. Preferred agent for bulk work.
|
|
4
|
+
# agy -p "prompt" --output-format json -> {status:"SUCCESS", response:"..."}
|
|
5
|
+
# Docs: https://antigravity.google/docs/cli/headless
|
|
6
|
+
- name: agy
|
|
7
|
+
binary: agy
|
|
8
|
+
flags: --output-format json -p
|
|
5
9
|
priority: 1
|
|
6
10
|
enabled: true
|
|
7
11
|
exec: flag
|
|
8
12
|
|
|
9
|
-
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
priority: 2
|
|
14
|
-
enabled: true
|
|
15
|
-
exec: flag
|
|
16
|
-
|
|
13
|
+
# `-p` MUST be present (non-interactive requires --print) and MUST be LAST:
|
|
14
|
+
# `--tools` is variadic (`<tools...>`) in the shipped build, so anything after
|
|
15
|
+
# it — including the appended prompt — is swallowed as a tool name. Proven
|
|
16
|
+
# both ways on claude v2.1.201.
|
|
17
17
|
- name: claude
|
|
18
18
|
binary: claude
|
|
19
|
-
flags: --output-format json --permission-mode bypassPermissions --setting-sources local --tools ""
|
|
20
|
-
priority:
|
|
19
|
+
flags: --output-format json --permission-mode bypassPermissions --setting-sources local --tools "" -p
|
|
20
|
+
priority: 2
|
|
21
21
|
enabled: true
|
|
22
22
|
exec: flag
|
|
23
|
+
|
|
24
|
+
# `-m` is REQUIRED: ~/.codex/config.toml may pin a model the account cannot
|
|
25
|
+
# use (seen: gpt-5.6-sol -> HTTP 400 "not supported when using Codex with a
|
|
26
|
+
# ChatGPT account"). Account-supported slugs: ~/.codex/models_cache.json.
|
|
27
|
+
# `--full-auto` is hidden from --help but still works as a deprecated alias;
|
|
28
|
+
# the explicit `--sandbox` form is OpenAI's current recommendation.
|
|
23
29
|
- name: codex
|
|
24
30
|
binary: codex
|
|
25
|
-
flags: --sandbox workspace-write --json --skip-git-repo-check
|
|
26
|
-
priority:
|
|
31
|
+
flags: -m gpt-5.5 --sandbox workspace-write --json --skip-git-repo-check
|
|
32
|
+
priority: 3
|
|
27
33
|
enabled: true
|
|
28
34
|
exec: subcommand
|
|
35
|
+
|
|
36
|
+
# DISABLED — `antigravity` on PATH is total-recall/bin/antigravity.mjs, a thin
|
|
37
|
+
# wrapper that POSTs to generativelanguage.googleapis.com with GOOGLE_API_KEY,
|
|
38
|
+
# i.e. the METERED Gemini API, not the Ultra plan. Verified 2026-08-01: every
|
|
39
|
+
# call returned 429 "You exceeded your current quota". Use `agy` above.
|
|
40
|
+
- name: antigravity
|
|
41
|
+
binary: antigravity
|
|
42
|
+
flags: --sandbox=false --yolo -o json
|
|
43
|
+
priority: 8
|
|
44
|
+
enabled: false
|
|
45
|
+
exec: flag
|
|
46
|
+
|
|
47
|
+
# DISABLED — this user has no Gemini CLI. Do not enable because a `gemini`
|
|
48
|
+
# binary resolves on PATH; their Google surface is `agy`.
|
|
49
|
+
- name: gemini
|
|
50
|
+
binary: gemini
|
|
51
|
+
flags: --sandbox=false --yolo -o json
|
|
52
|
+
priority: 9
|
|
53
|
+
enabled: false
|
|
54
|
+
exec: flag
|
|
55
|
+
|
|
56
|
+
# DISABLED — not installed. A stale binary may still resolve at
|
|
57
|
+
# ~/.grok/bin/grok; PATH presence is not proof an agent works.
|
|
58
|
+
- name: grok
|
|
59
|
+
binary: grok
|
|
60
|
+
flags: --output-format plain --always-approve --permission-mode bypassPermissions
|
|
61
|
+
priority: 10
|
|
62
|
+
enabled: false
|
|
63
|
+
exec: flag
|
|
64
|
+
|
|
29
65
|
timeout: 300
|
|
30
66
|
max_retries: 2
|
package/src/cli/recall.mjs
CHANGED
|
@@ -128,6 +128,7 @@ export default async function recall(args) {
|
|
|
128
128
|
// Search all target brains and merge results
|
|
129
129
|
let allResults = [];
|
|
130
130
|
let isDegraded = false;
|
|
131
|
+
const searchErrors = [];
|
|
131
132
|
|
|
132
133
|
for (const target of searchTargets) {
|
|
133
134
|
const vaultDir = path.join(target.brainDir, 'memory-vault');
|
|
@@ -168,8 +169,16 @@ export default async function recall(args) {
|
|
|
168
169
|
r._layer = target.label;
|
|
169
170
|
}
|
|
170
171
|
allResults = allResults.concat(results);
|
|
171
|
-
} catch {
|
|
172
|
-
//
|
|
172
|
+
} catch (err) {
|
|
173
|
+
// A brain that cannot be searched is skipped — but NEVER silently.
|
|
174
|
+
//
|
|
175
|
+
// This used to be a bare `catch {}`. A missing native binding for
|
|
176
|
+
// better-sqlite3 ("Could not locate the bindings file") made every
|
|
177
|
+
// semantic search throw, and the swallow turned a hard dependency
|
|
178
|
+
// crash into the message "No matching memory nodes found" — so a
|
|
179
|
+
// fully-populated vault looked empty and the real cause was invisible.
|
|
180
|
+
// Degrading to lexical-only results is fine; hiding why is not.
|
|
181
|
+
searchErrors.push({ label: target.label, message: String(err?.message || err) });
|
|
173
182
|
}
|
|
174
183
|
}
|
|
175
184
|
|
|
@@ -182,8 +191,27 @@ export default async function recall(args) {
|
|
|
182
191
|
return;
|
|
183
192
|
}
|
|
184
193
|
|
|
194
|
+
if (searchErrors.length > 0) {
|
|
195
|
+
// Surface the real cause. "No matches" and "search crashed" are very
|
|
196
|
+
// different problems and must never look the same.
|
|
197
|
+
console.log('');
|
|
198
|
+
for (const e of searchErrors) {
|
|
199
|
+
console.log(` ⚠️ ${e.label} brain search failed: ${e.message.split('\n')[0]}`);
|
|
200
|
+
}
|
|
201
|
+
if (/bindings file|\.node|NODE_MODULE_VERSION|better-sqlite3/i.test(searchErrors.map(e => e.message).join(' '))) {
|
|
202
|
+
console.log(' A native module is missing or was built for a different Node version.');
|
|
203
|
+
console.log(' Fix: pnpm rebuild better-sqlite3 (or npm rebuild better-sqlite3)');
|
|
204
|
+
console.log(' With pnpm, the package must also be allowed to run build scripts.');
|
|
205
|
+
}
|
|
206
|
+
console.log(' Results below (if any) are lexical-only and may be incomplete.');
|
|
207
|
+
}
|
|
208
|
+
|
|
185
209
|
if (allResults.length === 0) {
|
|
186
|
-
console.log(
|
|
210
|
+
console.log(
|
|
211
|
+
searchErrors.length > 0
|
|
212
|
+
? '\n 🔍 No results — search failed above, so this is NOT proof the vault is empty.'
|
|
213
|
+
: '\n 🔍 No matching memory nodes found.',
|
|
214
|
+
);
|
|
187
215
|
return;
|
|
188
216
|
}
|
|
189
217
|
if (isDegraded) {
|
package/src/core/runtime.mjs
CHANGED
|
@@ -47,11 +47,21 @@ export function findBinaryInPath(binaryName) {
|
|
|
47
47
|
|
|
48
48
|
// ─── Default agent registry (used when agents.yml doesn't exist) ─────────────
|
|
49
49
|
|
|
50
|
+
// NOTE ON FLAG ORDER AND MODEL PINS (verified by live probe 2026-08-01):
|
|
51
|
+
// agy — the REAL Google Antigravity CLI (AI Ultra). The binary named
|
|
52
|
+
// `antigravity` is a metered Gemini API wrapper, NOT this. Disabled
|
|
53
|
+
// below; enabling it bills an exhausted API budget (429s).
|
|
54
|
+
// claude — `-p` is required for non-interactive AND must be LAST, because
|
|
55
|
+
// `--tools` is variadic and otherwise swallows the prompt.
|
|
56
|
+
// codex — `-m` must be pinned; a config.toml model the account cannot use
|
|
57
|
+
// fails with HTTP 400 at exit code 0.
|
|
50
58
|
const DEFAULT_AGENTS = [
|
|
51
|
-
{ name: '
|
|
52
|
-
{ name: '
|
|
53
|
-
{ name: '
|
|
54
|
-
{ name: '
|
|
59
|
+
{ name: 'agy', binary: 'agy', flags: '--output-format json -p', priority: 1, enabled: true, exec: 'flag' },
|
|
60
|
+
{ name: 'claude', binary: 'claude', flags: '--output-format json --permission-mode bypassPermissions --setting-sources local --tools "" -p', priority: 2, enabled: true, exec: 'flag' },
|
|
61
|
+
{ name: 'codex', binary: 'codex', flags: '-m gpt-5.5 --sandbox workspace-write --json --skip-git-repo-check', priority: 3, enabled: true, exec: 'subcommand' },
|
|
62
|
+
{ name: 'antigravity', binary: 'antigravity', flags: '--sandbox=false --yolo -o json', priority: 8, enabled: false, exec: 'flag' },
|
|
63
|
+
{ name: 'gemini', binary: 'gemini', flags: '--sandbox=false --yolo -o json', priority: 9, enabled: false, exec: 'flag' },
|
|
64
|
+
{ name: 'grok', binary: 'grok', flags: '--output-format plain --always-approve --permission-mode bypassPermissions', priority: 10, enabled: false, exec: 'flag' },
|
|
55
65
|
];
|
|
56
66
|
|
|
57
67
|
/**
|
|
@@ -299,29 +309,65 @@ export async function callLocalRuntime(prompt, system, config) {
|
|
|
299
309
|
}
|
|
300
310
|
} catch {}
|
|
301
311
|
|
|
302
|
-
// Try agents in priority order
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
312
|
+
// Try agents in priority order, never re-trying one that already failed.
|
|
313
|
+
//
|
|
314
|
+
// The old filter excluded only `lastError.agentName` — the MOST RECENT
|
|
315
|
+
// failure — so a chain of three could hand work back to an agent that had
|
|
316
|
+
// already failed while a healthy one further down was never reached. Track
|
|
317
|
+
// every failure instead.
|
|
318
|
+
//
|
|
319
|
+
// The iteration cap is `agents.length + maxRetries` so that each enabled
|
|
320
|
+
// agent gets a turn even when the retry budget is smaller than the chain;
|
|
321
|
+
// the loop exits as soon as the untried pool is empty.
|
|
322
|
+
const failedAgents = new Set();
|
|
323
|
+
const maxAttempts = agents.length + maxRetries;
|
|
324
|
+
|
|
325
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
326
|
+
const remaining = agents.filter(a => !failedAgents.has(a.name));
|
|
327
|
+
const agent = remaining.length > 0
|
|
328
|
+
? resolveAgent({ ...config, agents: remaining })
|
|
329
|
+
: null;
|
|
308
330
|
|
|
309
331
|
if (!agent) {
|
|
310
|
-
|
|
332
|
+
if (failedAgents.size > 0) {
|
|
333
|
+
throw new Error(
|
|
334
|
+
`All CLI agents failed (tried: ${[...failedAgents].join(', ')}). ` +
|
|
335
|
+
`Last error: ${lastError?.error || 'unknown'}`,
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
throw new Error(
|
|
339
|
+
'No CLI agents available. Install agy (Google Antigravity), claude, or codex.',
|
|
340
|
+
);
|
|
311
341
|
}
|
|
312
342
|
|
|
343
|
+
// The prompt goes LAST, after every flag. Pushing it first (the old
|
|
344
|
+
// behaviour) broke the registry's own flag sets: it emitted a duplicate
|
|
345
|
+
// `-p` for agents whose flags already carry one, and it defeats the
|
|
346
|
+
// ordering that keeps a variadic flag (claude's `--tools <tools...>`) from
|
|
347
|
+
// swallowing the prompt as one of its values.
|
|
313
348
|
const args = [];
|
|
314
349
|
if (agent.exec === 'subcommand') {
|
|
315
|
-
args.push('exec'
|
|
316
|
-
} else {
|
|
317
|
-
args.push('-p', fullPrompt);
|
|
350
|
+
args.push('exec');
|
|
318
351
|
}
|
|
319
352
|
if (agent.flags) {
|
|
320
|
-
|
|
353
|
+
// spawnSync does not go through a shell, so quotes in the registry are
|
|
354
|
+
// literal characters: `--tools ""` would pass a two-character string
|
|
355
|
+
// rather than an empty one. Strip matched surrounding quotes per token.
|
|
356
|
+
args.push(
|
|
357
|
+
...agent.flags
|
|
358
|
+
.split(/\s+/)
|
|
359
|
+
.filter(Boolean)
|
|
360
|
+
.map(tok => tok.replace(/^(['"])(.*)\1$/, '$2')),
|
|
361
|
+
);
|
|
321
362
|
}
|
|
322
363
|
if (agent.model) {
|
|
323
364
|
args.push('-m', agent.model);
|
|
324
365
|
}
|
|
366
|
+
// `exec: flag` agents need an explicit `-p` only if their flags omit it.
|
|
367
|
+
if (agent.exec !== 'subcommand' && !args.includes('-p') && !args.includes('--print')) {
|
|
368
|
+
args.push('-p');
|
|
369
|
+
}
|
|
370
|
+
args.push(fullPrompt);
|
|
325
371
|
|
|
326
372
|
const cmd = `${agent.binaryPath} ${args.map(a => a.includes(' ') || a.includes('\n') ? `"${a.replace(/"/g, '\\"')}"` : a).join(' ')}`;
|
|
327
373
|
|
|
@@ -373,11 +419,27 @@ export async function callLocalRuntime(prompt, system, config) {
|
|
|
373
419
|
cwd: process.cwd(),
|
|
374
420
|
});
|
|
375
421
|
|
|
376
|
-
|
|
422
|
+
const softFailure = detectAgentFailure(result.stdout || '');
|
|
423
|
+
|
|
424
|
+
if (result.status === 0 && !softFailure) {
|
|
377
425
|
const output = result.stdout?.trim() || '';
|
|
378
426
|
return parseAgentOutput(output);
|
|
379
427
|
}
|
|
380
428
|
|
|
429
|
+
if (softFailure) {
|
|
430
|
+
// Exit code 0 but the payload says the run failed. Auth and model-config
|
|
431
|
+
// failures are not transient, so record and move to the next agent
|
|
432
|
+
// instead of burning the remaining retries on the same broken agent.
|
|
433
|
+
logger.error({
|
|
434
|
+
subsystem: 'runtime',
|
|
435
|
+
message: `${agent.name} reported failure despite exit 0`,
|
|
436
|
+
error: softFailure.slice(0, 500),
|
|
437
|
+
});
|
|
438
|
+
lastError = { agentName: agent.name, error: softFailure.slice(0, 500) };
|
|
439
|
+
failedAgents.add(agent.name);
|
|
440
|
+
continue;
|
|
441
|
+
}
|
|
442
|
+
|
|
381
443
|
const err = result.stderr || result.stdout || 'Unknown error';
|
|
382
444
|
logger.error({
|
|
383
445
|
subsystem: 'runtime',
|
|
@@ -385,9 +447,13 @@ export async function callLocalRuntime(prompt, system, config) {
|
|
|
385
447
|
error: err.slice(0, 500),
|
|
386
448
|
});
|
|
387
449
|
lastError = { agentName: agent.name, error: err.slice(0, 500) };
|
|
450
|
+
failedAgents.add(agent.name);
|
|
388
451
|
}
|
|
389
452
|
|
|
390
|
-
throw new Error(
|
|
453
|
+
throw new Error(
|
|
454
|
+
`All CLI agents failed (tried: ${[...failedAgents].join(', ') || 'none'}). ` +
|
|
455
|
+
`Last error: ${lastError?.error || 'unknown'}`,
|
|
456
|
+
);
|
|
391
457
|
}
|
|
392
458
|
|
|
393
459
|
/**
|
|
@@ -406,6 +472,57 @@ export async function callLocalRuntimeRaw(messages, config, _tools = undefined)
|
|
|
406
472
|
/**
|
|
407
473
|
* Parse JSON or text output from a CLI agent into plain text.
|
|
408
474
|
*/
|
|
475
|
+
/**
|
|
476
|
+
* Control-frame `type` values in codex's `--json` event stream. None of them
|
|
477
|
+
* carry the model's answer.
|
|
478
|
+
*/
|
|
479
|
+
const STREAM_CONTROL_TYPES = new Set([
|
|
480
|
+
'thread.started', 'turn.started', 'turn.completed', 'turn.failed',
|
|
481
|
+
'item.started', 'item.updated', 'item.completed', 'error',
|
|
482
|
+
]);
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* Detects a failure the exit code did not report.
|
|
486
|
+
*
|
|
487
|
+
* CLI agents exit 0 on failed runs and describe the failure only in the
|
|
488
|
+
* payload (verified 2026-08-01):
|
|
489
|
+
* claude → {"subtype":"success","is_error":true,"result":"Not logged in …"}
|
|
490
|
+
* codex → {"type":"turn.failed","error":{"message":"…"}}
|
|
491
|
+
* agy → {"status":"ERROR"|"CANCELED","error":"…"}
|
|
492
|
+
*
|
|
493
|
+
* Without this, `status === 0` accepts the error text as the model's answer —
|
|
494
|
+
* e.g. "Not logged in · Please run /login" gets stored as a memory node.
|
|
495
|
+
*
|
|
496
|
+
* @param {string} output raw stdout
|
|
497
|
+
* @returns {string|null} failure reason, or null if the run looks clean
|
|
498
|
+
*/
|
|
499
|
+
function detectAgentFailure(output) {
|
|
500
|
+
if (!output) return null;
|
|
501
|
+
for (const line of output.split('\n')) {
|
|
502
|
+
const t = line.trim();
|
|
503
|
+
if (!t.startsWith('{')) continue;
|
|
504
|
+
let parsed;
|
|
505
|
+
try {
|
|
506
|
+
parsed = JSON.parse(t);
|
|
507
|
+
} catch {
|
|
508
|
+
continue;
|
|
509
|
+
}
|
|
510
|
+
if (parsed.is_error === true) {
|
|
511
|
+
return typeof parsed.result === 'string' ? parsed.result : 'is_error: true';
|
|
512
|
+
}
|
|
513
|
+
if (parsed.type === 'turn.failed' || parsed.type === 'error') {
|
|
514
|
+
const msg = parsed.error?.message ?? parsed.message;
|
|
515
|
+
return typeof msg === 'string' ? msg : String(parsed.type);
|
|
516
|
+
}
|
|
517
|
+
if (typeof parsed.status === 'string' && parsed.status.toUpperCase() !== 'SUCCESS') {
|
|
518
|
+
return typeof parsed.error === 'string'
|
|
519
|
+
? `${parsed.status}: ${parsed.error}`
|
|
520
|
+
: `status=${parsed.status}`;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
return null;
|
|
524
|
+
}
|
|
525
|
+
|
|
409
526
|
function parseAgentOutput(output) {
|
|
410
527
|
try {
|
|
411
528
|
const parsed = JSON.parse(output);
|
|
@@ -418,7 +535,34 @@ function parseAgentOutput(output) {
|
|
|
418
535
|
return last.content || last.text || last.response || JSON.stringify(last);
|
|
419
536
|
}
|
|
420
537
|
} catch {
|
|
421
|
-
// Not JSON
|
|
538
|
+
// Not a single JSON document. It may still be a JSONL event stream —
|
|
539
|
+
// codex `--json` emits one frame per line, so a whole-output JSON.parse
|
|
540
|
+
// always throws and the raw stream used to be returned as the "answer".
|
|
541
|
+
const answers = [];
|
|
542
|
+
for (const line of output.split('\n')) {
|
|
543
|
+
const t = line.trim();
|
|
544
|
+
if (!t.startsWith('{')) continue;
|
|
545
|
+
let frame;
|
|
546
|
+
try {
|
|
547
|
+
frame = JSON.parse(t);
|
|
548
|
+
} catch {
|
|
549
|
+
continue;
|
|
550
|
+
}
|
|
551
|
+
// codex: the reply is item.text of the `agent_message` item.
|
|
552
|
+
if (frame.item?.type === 'agent_message' && typeof frame.item.text === 'string') {
|
|
553
|
+
answers.push(frame.item.text);
|
|
554
|
+
continue;
|
|
555
|
+
}
|
|
556
|
+
if (STREAM_CONTROL_TYPES.has(frame.type)) continue;
|
|
557
|
+
for (const key of ['response', 'result', 'content', 'text']) {
|
|
558
|
+
if (typeof frame[key] === 'string') {
|
|
559
|
+
answers.push(frame[key]);
|
|
560
|
+
break;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
// Last message wins: earlier frames are partial or superseded.
|
|
565
|
+
if (answers.length > 0) return answers[answers.length - 1];
|
|
422
566
|
}
|
|
423
567
|
return output;
|
|
424
568
|
}
|
|
@@ -552,6 +552,23 @@ export function resetGateStats() {
|
|
|
552
552
|
* Reset internal rate-limit/concurrency state (for testing only).
|
|
553
553
|
*/
|
|
554
554
|
export function resetGateStateForTests() {
|
|
555
|
+
// Tear down policy watchers FIRST. They are module-level singletons that
|
|
556
|
+
// outlive an individual test, and their callbacks call applyPolicy() +
|
|
557
|
+
// drainQueue() — so a watcher registered by an earlier test can fire in the
|
|
558
|
+
// middle of a later one, silently rewriting blockedDomains/domainMinInterval
|
|
559
|
+
// and draining the queue. That made the firewall-reject and minInterval
|
|
560
|
+
// tests fail intermittently under full-suite load while passing in
|
|
561
|
+
// isolation. Closing them here makes each test start from a quiet state.
|
|
562
|
+
for (const w of [policyWatcher, parentWatcher]) {
|
|
563
|
+
try {
|
|
564
|
+
w?.close();
|
|
565
|
+
} catch {
|
|
566
|
+
// already closed / never opened
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
policyWatcher = null;
|
|
570
|
+
parentWatcher = null;
|
|
571
|
+
|
|
555
572
|
gateGeneration += 1;
|
|
556
573
|
globalInFlight = 0;
|
|
557
574
|
domainInFlight.clear();
|