larkin 0.4.28 → 0.4.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app/agent-config.mjs +3 -1
- package/dist/app/cli.mjs +13 -3
- package/dist/app/local-control.mjs +16 -3
- package/dist/app/pi-auth-cli.mjs +227 -17
- package/dist/app/runtime-process.mjs +42 -5
- package/dist/dashboard/dashboard-config-controller.mjs +130 -5
- package/dist/dashboard/web/assets/dashboard.css +1 -1
- package/dist/dashboard/web/assets/dashboard.js +15 -15
- package/dist/feishu/host-shell.cjs +68 -16
- package/dist/feishu/host-shell.mjs +69 -17
- package/dist/platform/config.cjs +38 -10
- package/dist/platform/config.mjs +36 -10
- package/dist/runtime/pi-official-auth.cjs +594 -0
- package/dist/runtime/pi-official-auth.mjs +53 -0
- package/dist/runtime/pi-provider-config.cjs +26 -3
- package/dist/runtime/pi-provider-config.mjs +22 -2
- package/dist/runtime/pi-provider-login.mjs +240 -0
- package/dist/runtime/runtime-adapters.mjs +24 -4
- package/dist/runtime/runtime-host.cjs +215 -16
- package/dist/runtime/runtime-host.mjs +216 -17
- package/dist/runtime/runtime-readiness.cjs +123 -3
- package/dist/runtime/runtime-readiness.mjs +116 -4
- package/package.json +1 -1
|
@@ -14,6 +14,7 @@ const inbox_projection_js_1 = require("../agent/inbox-projection.cjs");
|
|
|
14
14
|
const api_1 = require("@opentelemetry/api");
|
|
15
15
|
const provider_error_classifier_js_1 = require("./provider-error-classifier.cjs");
|
|
16
16
|
const runtime_readiness_js_1 = require("./runtime-readiness.cjs");
|
|
17
|
+
const pi_official_auth_js_1 = require("./pi-official-auth.cjs");
|
|
17
18
|
const official_lark_cli_js_1 = require("../app/official-lark-cli.cjs");
|
|
18
19
|
const agent_lark_cli_workspace_js_1 = require("../app/agent-lark-cli-workspace.cjs");
|
|
19
20
|
const pi_compaction_recovery_js_1 = require("./pi-compaction-recovery.cjs");
|
|
@@ -102,6 +103,121 @@ function persistPreviousSession(agent, previous) {
|
|
|
102
103
|
// In-memory ref still holds for this process; restart will miss it only if this write failed.
|
|
103
104
|
}
|
|
104
105
|
}
|
|
106
|
+
function isBuiltinPiAgent(agent) {
|
|
107
|
+
return agent.adapter.id === "pi"
|
|
108
|
+
&& ((0, runtime_readiness_js_1.isBuiltinPiDistribution)(agent.config.piDistribution) || agent.config.runtime === "builtin-pi");
|
|
109
|
+
}
|
|
110
|
+
function authFailureScopeOf(agent) {
|
|
111
|
+
return {
|
|
112
|
+
runtime: agent.config.runtime,
|
|
113
|
+
model: agent.config.model,
|
|
114
|
+
piDistribution: agent.config.piDistribution,
|
|
115
|
+
adapterId: agent.adapter.id,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function currentAuthFailure(agent) {
|
|
119
|
+
if (!agent.authFailureActive)
|
|
120
|
+
return null;
|
|
121
|
+
const kind = agent.authFailureKind
|
|
122
|
+
?? (agent.authFailureProvider ? "missing-provider" : "generic");
|
|
123
|
+
if (kind === "missing-provider") {
|
|
124
|
+
if (!agent.authFailureProvider)
|
|
125
|
+
return null;
|
|
126
|
+
return { kind: "missing-provider", runtime: "pi", piDistribution: "builtin", provider: agent.authFailureProvider };
|
|
127
|
+
}
|
|
128
|
+
const runtime = agent.adapter.id === "codex" || agent.adapter.id === "claude" || agent.adapter.id === "pi"
|
|
129
|
+
? agent.adapter.id : null;
|
|
130
|
+
if (!runtime)
|
|
131
|
+
return null;
|
|
132
|
+
return {
|
|
133
|
+
kind: "generic",
|
|
134
|
+
runtime,
|
|
135
|
+
...(runtime === "pi" && (agent.config.piDistribution === "builtin" || agent.config.piDistribution === "external")
|
|
136
|
+
? { piDistribution: agent.config.piDistribution } : {}),
|
|
137
|
+
provider: agent.authFailureProvider,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
function genericAuthProvider(agent, upstream) {
|
|
141
|
+
return (0, runtime_readiness_js_1.safeProviderId)(upstream?.provider) ?? (0, runtime_readiness_js_1.configuredProviderId)(agent.config.model);
|
|
142
|
+
}
|
|
143
|
+
function persistAuthFailure(agent) {
|
|
144
|
+
const store = agent.stateStore;
|
|
145
|
+
if (!store)
|
|
146
|
+
return;
|
|
147
|
+
try {
|
|
148
|
+
const write = () => {
|
|
149
|
+
const current = store.readJson("agentState", { sessions: {} });
|
|
150
|
+
if (Array.isArray(current.records) && !("sessions" in current))
|
|
151
|
+
return;
|
|
152
|
+
const { authFailureProvider: _legacy, authFailure: _scoped, ...rest } = current;
|
|
153
|
+
const failure = currentAuthFailure(agent);
|
|
154
|
+
if (!failure) {
|
|
155
|
+
if (!("authFailureProvider" in current) && !("authFailure" in current))
|
|
156
|
+
return;
|
|
157
|
+
store.writeJson("agentState", rest);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
store.writeJson("agentState", {
|
|
161
|
+
...rest,
|
|
162
|
+
authFailure: failure,
|
|
163
|
+
...(failure.kind === "missing-provider" && failure.provider
|
|
164
|
+
? { authFailureProvider: failure.provider } : {}),
|
|
165
|
+
});
|
|
166
|
+
};
|
|
167
|
+
if (store.withInboxTransaction)
|
|
168
|
+
store.withInboxTransaction(write);
|
|
169
|
+
else
|
|
170
|
+
write();
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
// In-memory flags and the delivery ledger still hold for this process.
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
function loadPersistedAuthFailure(store) {
|
|
177
|
+
if (!store)
|
|
178
|
+
return null;
|
|
179
|
+
try {
|
|
180
|
+
return (0, runtime_readiness_js_1.parsePersistedAuthFailure)(store.readJson("agentState", {}));
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function applyAuthFailure(agent, failure) {
|
|
187
|
+
agent.authFailureActive = true;
|
|
188
|
+
agent.authFailureKind = failure.kind;
|
|
189
|
+
agent.authFailureProvider = failure.provider ?? null;
|
|
190
|
+
agent.readiness = (0, runtime_readiness_js_1.readinessForPersistedAuthFailure)(failure);
|
|
191
|
+
}
|
|
192
|
+
function clearAuthFailure(agent, persist = true) {
|
|
193
|
+
agent.authFailureActive = false;
|
|
194
|
+
agent.authFailureKind = null;
|
|
195
|
+
agent.authFailureProvider = null;
|
|
196
|
+
if (persist)
|
|
197
|
+
persistAuthFailure(agent);
|
|
198
|
+
}
|
|
199
|
+
function restoreAuthFailure(agent) {
|
|
200
|
+
const persisted = loadPersistedAuthFailure(agent.stateStore);
|
|
201
|
+
if (!persisted)
|
|
202
|
+
return;
|
|
203
|
+
// 只恢复仍绑定当前 candidate 的当前作用域 auth;不从历史 delivery 回灌。
|
|
204
|
+
if (!(0, runtime_readiness_js_1.authFailureAppliesTo)(authFailureScopeOf(agent), persisted)) {
|
|
205
|
+
clearAuthFailure(agent);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
applyAuthFailure(agent, persisted);
|
|
209
|
+
}
|
|
210
|
+
function clearLedgerAuthProviders(agent) {
|
|
211
|
+
let changed = false;
|
|
212
|
+
for (const record of agent.records.values()) {
|
|
213
|
+
if (!record.authProvider)
|
|
214
|
+
continue;
|
|
215
|
+
delete record.authProvider;
|
|
216
|
+
changed = true;
|
|
217
|
+
}
|
|
218
|
+
if (changed)
|
|
219
|
+
persist(agent);
|
|
220
|
+
}
|
|
105
221
|
const REPLAY_FAILURE_CODES = new Set([
|
|
106
222
|
"canonical_inbox_row_missing", "canonical_inbox_malformed", "duplicate_message_id",
|
|
107
223
|
"inbox_state_conflict", "delivery_target_conflict", "structured_target_invalid",
|
|
@@ -490,6 +606,34 @@ function createRuntimeHost(options) {
|
|
|
490
606
|
messageId: record.messageId, status: "consumed" });
|
|
491
607
|
}
|
|
492
608
|
};
|
|
609
|
+
const ownedConfigDir = () => {
|
|
610
|
+
const value = process.env.LARKIN_CONFIG_DIR?.trim();
|
|
611
|
+
return value || null;
|
|
612
|
+
};
|
|
613
|
+
const projectAuthFailure = (agent, readiness, failure) => {
|
|
614
|
+
agent.authFailureActive = true;
|
|
615
|
+
agent.authFailureKind = failure.kind;
|
|
616
|
+
agent.authFailureProvider = failure.provider;
|
|
617
|
+
agent.readiness = {
|
|
618
|
+
...readiness,
|
|
619
|
+
...(agent.readiness?.executable ? { executable: agent.readiness.executable } : {}),
|
|
620
|
+
...(agent.readiness?.version ? { version: agent.readiness.version } : {}),
|
|
621
|
+
};
|
|
622
|
+
persistAuthFailure(agent);
|
|
623
|
+
emit({ type: "agent-status", agentId: agent.config.agentId, status: "error",
|
|
624
|
+
error: `${agent.readiness.reason} ${agent.readiness.nextAction}`, readiness: agent.readiness });
|
|
625
|
+
};
|
|
626
|
+
const namedAuthProviderPresent = (agent) => {
|
|
627
|
+
if (agent.authFailureKind !== "missing-provider" || !agent.authFailureProvider)
|
|
628
|
+
return true;
|
|
629
|
+
const failure = currentAuthFailure(agent);
|
|
630
|
+
if (!failure || !(0, runtime_readiness_js_1.authFailureAppliesTo)(authFailureScopeOf(agent), failure))
|
|
631
|
+
return false;
|
|
632
|
+
const configDir = ownedConfigDir();
|
|
633
|
+
if (!configDir)
|
|
634
|
+
return false;
|
|
635
|
+
return (0, pi_official_auth_js_1.officialPiHasStoredProvider)(configDir, agent.config.agentId, agent.authFailureProvider);
|
|
636
|
+
};
|
|
493
637
|
const resultFor = (agent, record, result) => {
|
|
494
638
|
if (result.status === "accepted") {
|
|
495
639
|
if (record.errorCategory !== "context_window") {
|
|
@@ -503,6 +647,28 @@ function createRuntimeHost(options) {
|
|
|
503
647
|
deliveryId: record.deliveryId, messageId: record.messageId, status: "accepted" });
|
|
504
648
|
return { status: "accepted", deliveryId: record.deliveryId };
|
|
505
649
|
}
|
|
650
|
+
const missing = result.status === "rejected" && isBuiltinPiAgent(agent)
|
|
651
|
+
? (0, runtime_readiness_js_1.classifyPiMissingCredentialRejection)(result.reason)
|
|
652
|
+
: null;
|
|
653
|
+
if (missing) {
|
|
654
|
+
const readiness = (0, runtime_readiness_js_1.missingProviderCredentialReadiness)(agent.adapter.id, missing.provider);
|
|
655
|
+
const reason = readiness.reason ?? `Provider ${missing.provider} is missing from this Agent's official credential store.`;
|
|
656
|
+
if (record.status !== "error") {
|
|
657
|
+
record.reason = reason;
|
|
658
|
+
record.retryable = false;
|
|
659
|
+
record.errorCategory = "auth";
|
|
660
|
+
record.authProvider = missing.provider;
|
|
661
|
+
const finalRecord = setRecord(agent, record, "error");
|
|
662
|
+
if (finalRecord.status !== "consumed")
|
|
663
|
+
emit({ type: "delivery", agentId: agent.config.agentId,
|
|
664
|
+
deliveryId: record.deliveryId, messageId: record.messageId, status: "error", reason });
|
|
665
|
+
projectAuthFailure(agent, readiness, { kind: "missing-provider", provider: missing.provider });
|
|
666
|
+
}
|
|
667
|
+
return { status: "error", deliveryId: record.deliveryId, reason: record.reason ?? reason, retryable: false };
|
|
668
|
+
}
|
|
669
|
+
if (record.status === "error") {
|
|
670
|
+
return { status: "error", deliveryId: record.deliveryId, reason: record.reason ?? result.reason, retryable: false };
|
|
671
|
+
}
|
|
506
672
|
if (record.errorCategory !== "context_window")
|
|
507
673
|
delete record.errorCategory;
|
|
508
674
|
const retryable = result.status === "deferred" || result.retryable;
|
|
@@ -1347,7 +1513,7 @@ function createRuntimeHost(options) {
|
|
|
1347
1513
|
agent.compactionMachines.delete(inputId);
|
|
1348
1514
|
}
|
|
1349
1515
|
const recoveredAuthentication = agent.authFailureActive && agent.turnInProgress
|
|
1350
|
-
&& agent.turnHadAuthenticatedOutput && !agent.turnHadFailure;
|
|
1516
|
+
&& agent.turnHadAuthenticatedOutput && !agent.turnHadFailure && namedAuthProviderPresent(agent);
|
|
1351
1517
|
agent.turnInProgress = false;
|
|
1352
1518
|
agent.busy = false;
|
|
1353
1519
|
emit({ type: "activity", agentId: agent.config.agentId, activity: "idle", activityKind: "idle", detailKind: "turn_ended" });
|
|
@@ -1369,7 +1535,8 @@ function createRuntimeHost(options) {
|
|
|
1369
1535
|
reconcileSubagentLedger(agent);
|
|
1370
1536
|
telemetry?.runtimeEvent(agent.config.agentId, event);
|
|
1371
1537
|
if (recoveredAuthentication) {
|
|
1372
|
-
agent
|
|
1538
|
+
clearAuthFailure(agent);
|
|
1539
|
+
clearLedgerAuthProviders(agent);
|
|
1373
1540
|
const prior = agent.readiness;
|
|
1374
1541
|
agent.readiness = {
|
|
1375
1542
|
runtime: agent.adapter.id,
|
|
@@ -1462,21 +1629,24 @@ function createRuntimeHost(options) {
|
|
|
1462
1629
|
record.errorCategory = classifiedCategory;
|
|
1463
1630
|
else
|
|
1464
1631
|
delete record.errorCategory;
|
|
1632
|
+
const missing = event.errorCategory === "auth" && !event.retryable && isBuiltinPiAgent(agent)
|
|
1633
|
+
? (0, runtime_readiness_js_1.classifyPiMissingCredentialRejection)(event.upstream?.message)
|
|
1634
|
+
?? (0, runtime_readiness_js_1.classifyPiMissingCredentialRejection)(event.message)
|
|
1635
|
+
: null;
|
|
1636
|
+
if (missing)
|
|
1637
|
+
record.authProvider = missing.provider;
|
|
1465
1638
|
const finalRecord = setRecord(agent, record, event.retryable ? "pending" : "error");
|
|
1466
1639
|
if (finalRecord.status !== "consumed")
|
|
1467
1640
|
emit({ type: "delivery", agentId: agent.config.agentId,
|
|
1468
1641
|
deliveryId: record.deliveryId, messageId: record.messageId,
|
|
1469
1642
|
status: event.retryable ? "deferred" : "error", reason: event.message });
|
|
1470
1643
|
if (event.errorCategory === "auth" && !event.retryable) {
|
|
1471
|
-
const readiness =
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
};
|
|
1478
|
-
emit({ type: "agent-status", agentId: agent.config.agentId, status: "error",
|
|
1479
|
-
error: `${agent.readiness.reason} ${agent.readiness.nextAction}`, readiness: agent.readiness });
|
|
1644
|
+
const readiness = missing
|
|
1645
|
+
? (0, runtime_readiness_js_1.missingProviderCredentialReadiness)(agent.adapter.id, missing.provider)
|
|
1646
|
+
: (0, runtime_readiness_js_1.providerAuthenticationFailureReadiness)(agent.adapter.id, event.upstream?.provider);
|
|
1647
|
+
projectAuthFailure(agent, readiness, missing
|
|
1648
|
+
? { kind: "missing-provider", provider: missing.provider }
|
|
1649
|
+
: { kind: "generic", provider: genericAuthProvider(agent, event.upstream) });
|
|
1480
1650
|
}
|
|
1481
1651
|
if (event.retryable && !agent.busy && !agent.turnInProgress)
|
|
1482
1652
|
void retryPending(agent);
|
|
@@ -1748,14 +1918,27 @@ function createRuntimeHost(options) {
|
|
|
1748
1918
|
clearTimeout(previous.backgroundCompletionRetryTimer);
|
|
1749
1919
|
if (previous.subagentReconcileTimer)
|
|
1750
1920
|
clearInterval(previous.subagentReconcileTimer);
|
|
1921
|
+
const previousFailure = currentAuthFailure(previous);
|
|
1922
|
+
const scoped = Boolean(previousFailure
|
|
1923
|
+
&& (0, runtime_readiness_js_1.authFailureAppliesTo)({
|
|
1924
|
+
runtime: config.runtime,
|
|
1925
|
+
model: config.model,
|
|
1926
|
+
piDistribution: config.piDistribution,
|
|
1927
|
+
adapterId: adapter.id,
|
|
1928
|
+
}, previousFailure));
|
|
1751
1929
|
const candidate = {
|
|
1752
1930
|
...previous, config, adapter, session, launchId: node_crypto_1.default.randomUUID(), busy: false, submitting: false,
|
|
1753
1931
|
starting: null, retryAfterSubmit: false, generation: 0, poller: null, retryTimer: null,
|
|
1754
1932
|
recreateAttempts: 0, stabilityTimer: null, recreateReason: null, stopped: false,
|
|
1755
1933
|
backgroundCompletionRetryTimer: null, subagentReconcileTimer: null,
|
|
1756
1934
|
disabledReason: null, configurationRecovery: null,
|
|
1757
|
-
|
|
1935
|
+
authFailureActive: scoped && previous.authFailureActive,
|
|
1936
|
+
authFailureKind: scoped ? previous.authFailureKind : null,
|
|
1937
|
+
authFailureProvider: scoped ? previous.authFailureProvider : null,
|
|
1938
|
+
readiness: scoped && previous.authFailureActive ? previous.readiness : readiness,
|
|
1758
1939
|
};
|
|
1940
|
+
if (previous.authFailureActive && !candidate.authFailureActive)
|
|
1941
|
+
clearAuthFailure(candidate);
|
|
1759
1942
|
managed.set(config.agentId, candidate);
|
|
1760
1943
|
session.subscribe((event) => observe(candidate, session, event));
|
|
1761
1944
|
if (session.sessionId) {
|
|
@@ -1850,7 +2033,8 @@ function createRuntimeHost(options) {
|
|
|
1850
2033
|
agent.config.sessionId = null;
|
|
1851
2034
|
agent.session = fresh;
|
|
1852
2035
|
persistPreviousSession(agent, previousRef);
|
|
1853
|
-
agent.
|
|
2036
|
+
if (!agent.authFailureActive)
|
|
2037
|
+
agent.readiness = readiness;
|
|
1854
2038
|
agent.disabledReason = null;
|
|
1855
2039
|
};
|
|
1856
2040
|
try {
|
|
@@ -1871,7 +2055,13 @@ function createRuntimeHost(options) {
|
|
|
1871
2055
|
...(fresh.effectiveReasoningEffort ? { reasoningEffort: fresh.effectiveReasoningEffort } : {}),
|
|
1872
2056
|
...(agent.previousSession ? { previousSession: agent.previousSession } : {}) });
|
|
1873
2057
|
}
|
|
1874
|
-
emit({
|
|
2058
|
+
emit({
|
|
2059
|
+
type: "agent-status",
|
|
2060
|
+
agentId,
|
|
2061
|
+
status: agent.authFailureActive ? "error" : "active",
|
|
2062
|
+
...(agent.authFailureActive ? { error: `${agent.readiness?.reason} ${agent.readiness?.nextAction}` } : {}),
|
|
2063
|
+
readiness: agent.authFailureActive ? agent.readiness ?? readiness : readiness,
|
|
2064
|
+
});
|
|
1875
2065
|
agent.piSessionOwner = probeEnv[pi_subagent_ledger_js_1.PI_SUBAGENT_SESSION_OWNER_ENV];
|
|
1876
2066
|
await oldSession.close("fresh session reset committed")
|
|
1877
2067
|
.catch((error) => log("previous runtime close after fresh reset failed", String(error)));
|
|
@@ -2009,7 +2199,8 @@ function createRuntimeHost(options) {
|
|
|
2009
2199
|
agent.config.sessionId = null;
|
|
2010
2200
|
agent.session = fresh;
|
|
2011
2201
|
persistPreviousSession(agent, previousRef);
|
|
2012
|
-
agent.
|
|
2202
|
+
if (!agent.authFailureActive)
|
|
2203
|
+
agent.readiness = readiness;
|
|
2013
2204
|
agent.disabledReason = null;
|
|
2014
2205
|
for (const record of agent.records.values()) {
|
|
2015
2206
|
if (!messageIds.includes(record.messageId))
|
|
@@ -2027,7 +2218,13 @@ function createRuntimeHost(options) {
|
|
|
2027
2218
|
...(fresh.effectiveReasoningEffort ? { reasoningEffort: fresh.effectiveReasoningEffort } : {}),
|
|
2028
2219
|
...(agent.previousSession ? { previousSession: agent.previousSession } : {}) });
|
|
2029
2220
|
}
|
|
2030
|
-
emit({
|
|
2221
|
+
emit({
|
|
2222
|
+
type: "agent-status",
|
|
2223
|
+
agentId,
|
|
2224
|
+
status: agent.authFailureActive ? "error" : "active",
|
|
2225
|
+
...(agent.authFailureActive ? { error: `${agent.readiness?.reason} ${agent.readiness?.nextAction}` } : {}),
|
|
2226
|
+
readiness: agent.authFailureActive ? agent.readiness ?? readiness : readiness,
|
|
2227
|
+
});
|
|
2031
2228
|
durableRollback = null;
|
|
2032
2229
|
agent.piSessionOwner = probeEnv[pi_subagent_ledger_js_1.PI_SUBAGENT_SESSION_OWNER_ENV];
|
|
2033
2230
|
await oldSession.close("context-window recovery committed")
|
|
@@ -2117,11 +2314,13 @@ function createRuntimeHost(options) {
|
|
|
2117
2314
|
piProactiveCompactionFailedGeneration: null,
|
|
2118
2315
|
previousSession: loadPersistedPreviousSession(config, stateStore), readiness: null,
|
|
2119
2316
|
turnInProgress: false, turnHadFailure: false, turnHadAuthenticatedOutput: false, authFailureActive: false,
|
|
2317
|
+
authFailureKind: null, authFailureProvider: null,
|
|
2120
2318
|
promotedInboxUpdateIds: new Set(),
|
|
2121
2319
|
backgroundCompletionQueue: [], backgroundCompletionKeys: new Set(),
|
|
2122
2320
|
backgroundCompletionInFlight: null, backgroundCompletionWakeInputId: null,
|
|
2123
2321
|
backgroundCompletionRejectStreak: 0, backgroundCompletionRetryTimer: null,
|
|
2124
2322
|
subagentLedger: loadSubagentLedger(config), subagentReconcileTimer: null };
|
|
2323
|
+
restoreAuthFailure(agent);
|
|
2125
2324
|
const startupConsumed = [];
|
|
2126
2325
|
const startupQuarantined = [];
|
|
2127
2326
|
for (const record of agent.records.values()) {
|