larkin 0.5.2 → 0.5.4
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/CHANGELOG.md +10 -0
- package/README.md +15 -0
- package/dist/agent/agent-cli-capabilities.cjs +1 -1
- package/dist/agent/agent-cli-capabilities.mjs +1 -1
- package/dist/agent/config-cli-contract.cjs +3 -1
- package/dist/agent/config-cli-contract.mjs +3 -1
- package/dist/agent/inbox-audit-heartbeat.cjs +157 -37
- package/dist/agent/inbox-audit-heartbeat.mjs +122 -36
- package/dist/agent/missed-outbound-scan.cjs +326 -54
- package/dist/agent/missed-outbound-scan.mjs +319 -53
- package/dist/app/agent-cli.mjs +37 -6
- package/dist/app/agent-config.mjs +36 -5
- package/dist/app/cli.mjs +1 -0
- package/dist/dashboard/dashboard-config-controller.mjs +15 -0
- package/dist/dashboard/web/assets/dashboard.css +1 -1
- package/dist/dashboard/web/assets/dashboard.js +15 -15
- package/dist/feishu/host-shell.cjs +110 -19
- package/dist/feishu/host-shell.mjs +113 -22
- package/dist/platform/config.cjs +173 -5
- package/dist/platform/config.mjs +167 -4
- package/dist/runtime/provider-error-classifier.cjs +14 -0
- package/dist/runtime/provider-error-classifier.mjs +13 -0
- package/dist/runtime/runtime-adapters.mjs +40 -13
- package/dist/runtime/runtime-host.cjs +46 -26
- package/dist/runtime/runtime-host.mjs +46 -26
- package/package.json +1 -1
|
@@ -57,6 +57,7 @@ const host_channel_business_js_1 = require("./host-channel-business.cjs");
|
|
|
57
57
|
const interaction_orchestrator_js_1 = require("./interaction-orchestrator.cjs");
|
|
58
58
|
const message_policy_js_1 = require("./message-policy.cjs");
|
|
59
59
|
const runtime_readiness_js_1 = require("../runtime/runtime-readiness.cjs");
|
|
60
|
+
const provider_error_classifier_js_1 = require("../runtime/provider-error-classifier.cjs");
|
|
60
61
|
const callback_capability_js_1 = require("../platform/callback-capability.cjs");
|
|
61
62
|
const config_js_1 = require("../platform/config.cjs");
|
|
62
63
|
const internal_command_js_1 = require("../app/internal-command.cjs");
|
|
@@ -119,6 +120,9 @@ function memberNamesFromPayloads(payloads) {
|
|
|
119
120
|
return names;
|
|
120
121
|
}
|
|
121
122
|
function errorMessage(error) { return error instanceof Error ? error.message : String(error); }
|
|
123
|
+
function safeRuntimeStatusDiagnostic(error) {
|
|
124
|
+
return (0, provider_error_classifier_js_1.safeProviderDiagnostic)(error, "Runtime entered an error state", 500);
|
|
125
|
+
}
|
|
122
126
|
function agentConfigSignature(agent) {
|
|
123
127
|
return JSON.stringify({
|
|
124
128
|
agentId: agent.agentId, runtime: agent.runtime, model: agent.model, effort: agent.effort ?? null,
|
|
@@ -233,17 +237,40 @@ function createHostShell({ env = process.env, runtimeHost, channelPackage, event
|
|
|
233
237
|
}
|
|
234
238
|
hostState.updateStatus(agent, { runtimeReadiness: { runtime: agent.runtime, state: "ready", observedAt } });
|
|
235
239
|
};
|
|
240
|
+
const projectFallbackRuntimeReadiness = (agent, observedAt, fallback, preserveCurrentUnavailableDiagnostic = false) => {
|
|
241
|
+
const failure = unresolvedCurrentAuth(agent);
|
|
242
|
+
if (failure) {
|
|
243
|
+
hostState.updateStatus(agent, {
|
|
244
|
+
runtimeReadiness: { ...(0, runtime_readiness_js_1.readinessForPersistedAuthFailure)(failure), observedAt },
|
|
245
|
+
});
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
const currentReadiness = hostState.readStatus(agent).runtimeReadiness;
|
|
249
|
+
const observedMs = Date.parse(String(currentReadiness?.observedAt || ""));
|
|
250
|
+
const daemonStartedMs = Date.parse(daemonStartedAt);
|
|
251
|
+
const currentEpoch = currentReadiness?.runtime === agent.runtime
|
|
252
|
+
&& Number.isFinite(observedMs) && Number.isFinite(daemonStartedMs) && observedMs >= daemonStartedMs;
|
|
253
|
+
if (currentEpoch && (currentReadiness?.state === "missing" || currentReadiness?.state === "unauthenticated" || currentReadiness?.state === "incompatible"))
|
|
254
|
+
return;
|
|
255
|
+
if (currentEpoch && preserveCurrentUnavailableDiagnostic && currentReadiness?.state === "unavailable"
|
|
256
|
+
&& typeof currentReadiness.reason === "string" && currentReadiness.reason.trim()) {
|
|
257
|
+
hostState.updateStatus(agent, {
|
|
258
|
+
runtimeReadiness: { ...currentReadiness, runtime: agent.runtime, state: "unavailable", observedAt },
|
|
259
|
+
});
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
hostState.updateStatus(agent, {
|
|
263
|
+
runtimeReadiness: { runtime: agent.runtime, ...fallback, observedAt },
|
|
264
|
+
});
|
|
265
|
+
};
|
|
236
266
|
const recordInboundDeliveryFailure = (agent, code) => {
|
|
237
267
|
const at = new Date().toISOString();
|
|
238
268
|
const reason = "Inbound Runtime delivery failed non-retryably; durable Inbox/ledger state is retained for recovery.";
|
|
239
269
|
const nextAction = "Inspect the delivery/status error, correct the Runtime or canonical Inbox state, then restart to replay safely.";
|
|
240
|
-
const currentReadiness = hostState.readStatus(agent).runtimeReadiness;
|
|
241
270
|
hostState.updateStatus(agent, {
|
|
242
271
|
inboundDeliveryHealth: { state: "error", code, at, reason, nextAction },
|
|
243
|
-
...(currentReadiness?.state === "unauthenticated" ? {} : {
|
|
244
|
-
runtimeReadiness: { runtime: agent.runtime, state: "incompatible", reason, nextAction },
|
|
245
|
-
}),
|
|
246
272
|
});
|
|
273
|
+
projectFallbackRuntimeReadiness(agent, at, { state: "unavailable", reason, nextAction }, true);
|
|
247
274
|
hostState.recordStatusError(agent, `${reason} ${nextAction} code=${code}`);
|
|
248
275
|
};
|
|
249
276
|
const agentStates = new Map();
|
|
@@ -397,13 +424,57 @@ function createHostShell({ env = process.env, runtimeHost, channelPackage, event
|
|
|
397
424
|
for (const agent of agents)
|
|
398
425
|
prepareAgentState(agent);
|
|
399
426
|
const reminder = new host_reminder_orchestrator_js_1.HostReminderOrchestrator({ agents, stateStore, envelopeProjector, deliveryTarget: runtimeHost, log });
|
|
427
|
+
const auditRegistry = (0, missed_outbound_scan_js_1.inboxAuditRegistryFile)(larkinHome);
|
|
428
|
+
const auditRetryJournal = (0, missed_outbound_scan_js_1.inboxAuditRetryFile)(larkinHome);
|
|
429
|
+
let auditRetryTimer = null;
|
|
430
|
+
let auditRetryAttempt = 0;
|
|
431
|
+
const reconcileAuditRetries = () => {
|
|
432
|
+
try {
|
|
433
|
+
return (0, missed_outbound_scan_js_1.reconcileInboxAuditTargetRetries)(auditRegistry, auditRetryJournal).remaining;
|
|
434
|
+
}
|
|
435
|
+
catch (error) {
|
|
436
|
+
log(`inbox audit retry 读取失败: ${(0, inbox_audit_heartbeat_js_1.boundedInboxAuditDiagnostic)(error)}`);
|
|
437
|
+
return 1;
|
|
438
|
+
}
|
|
439
|
+
};
|
|
440
|
+
const scheduleAuditRetry = () => {
|
|
441
|
+
if (auditRetryTimer || auditRetryAttempt >= 5)
|
|
442
|
+
return;
|
|
443
|
+
const delay = [100, 250, 1_000, 3_000, 10_000][auditRetryAttempt++];
|
|
444
|
+
auditRetryTimer = setTimeout(() => {
|
|
445
|
+
auditRetryTimer = null;
|
|
446
|
+
if (reconcileAuditRetries() > 0)
|
|
447
|
+
scheduleAuditRetry();
|
|
448
|
+
else
|
|
449
|
+
auditRetryAttempt = 0;
|
|
450
|
+
}, delay);
|
|
451
|
+
auditRetryTimer.unref?.();
|
|
452
|
+
};
|
|
400
453
|
const inboxAudit = new inbox_audit_heartbeat_js_1.InboxAuditHeartbeat({
|
|
401
454
|
agents,
|
|
402
455
|
stateStore,
|
|
403
456
|
runtimeHost,
|
|
404
457
|
log,
|
|
458
|
+
configFile: node_path_1.default.join(larkinHome, "config.json"),
|
|
459
|
+
schedule(agent) {
|
|
460
|
+
try {
|
|
461
|
+
return (0, config_js_1.resolveInboxAuditSchedule)((0, config_js_1.loadConfig)(env).config, agent.agentId);
|
|
462
|
+
}
|
|
463
|
+
catch (error) {
|
|
464
|
+
log(`inbox audit schedule 读取失败 agent=${agent.agentId}: ${errorMessage(error)}`);
|
|
465
|
+
return { enabled: false, intervalMs: inbox_audit_heartbeat_js_1.INBOX_AUDIT_CADENCE_MS };
|
|
466
|
+
}
|
|
467
|
+
},
|
|
468
|
+
shouldDispatch(agent) {
|
|
469
|
+
try {
|
|
470
|
+
return (0, missed_outbound_scan_js_1.hasPendingInboxAuditTargets)(auditRegistry, agent.agentId);
|
|
471
|
+
}
|
|
472
|
+
catch (error) {
|
|
473
|
+
log(`inbox audit pending 读取失败 agent=${agent.agentId}: ${errorMessage(error)}`);
|
|
474
|
+
return false;
|
|
475
|
+
}
|
|
476
|
+
},
|
|
405
477
|
});
|
|
406
|
-
const auditRegistry = (0, missed_outbound_scan_js_1.inboxAuditRegistryFile)(larkinHome);
|
|
407
478
|
const seenEventIds = new Set();
|
|
408
479
|
const inFlightEventIds = new Set();
|
|
409
480
|
const onFeishuMessage = async (agent, event, options) => {
|
|
@@ -450,14 +521,23 @@ function createHostShell({ env = process.env, runtimeHost, channelPackage, event
|
|
|
450
521
|
// append/dedupe decision is durable. Agent model-seen state is untouched.
|
|
451
522
|
if (event.event_id)
|
|
452
523
|
seenEventIds.add(eventKey);
|
|
524
|
+
if (append.status === "duplicate_consumed")
|
|
525
|
+
return null;
|
|
526
|
+
const auditSourceSeq = Number(append.envelope.target_seq);
|
|
453
527
|
try {
|
|
454
|
-
(0, missed_outbound_scan_js_1.observeInboxAuditTarget)(auditRegistry, agent.agentId, event);
|
|
528
|
+
(0, missed_outbound_scan_js_1.observeInboxAuditTarget)(auditRegistry, agent.agentId, { ...event, wake, source_seq: auditSourceSeq });
|
|
529
|
+
(0, missed_outbound_scan_js_1.discardInboxAuditTargetRetry)(auditRetryJournal, agent.agentId, { ...event, source_seq: auditSourceSeq });
|
|
455
530
|
}
|
|
456
531
|
catch (error) {
|
|
457
|
-
|
|
532
|
+
try {
|
|
533
|
+
(0, missed_outbound_scan_js_1.enqueueInboxAuditTargetRetry)(auditRetryJournal, agent.agentId, { ...event, wake, source_seq: auditSourceSeq });
|
|
534
|
+
scheduleAuditRetry();
|
|
535
|
+
log(`inbox audit target 延后重试: ${(0, inbox_audit_heartbeat_js_1.boundedInboxAuditDiagnostic)(error)}`);
|
|
536
|
+
}
|
|
537
|
+
catch (retryError) {
|
|
538
|
+
log(`inbox audit target 未持久化: ${(0, inbox_audit_heartbeat_js_1.boundedInboxAuditDiagnostic)(retryError)}`);
|
|
539
|
+
}
|
|
458
540
|
}
|
|
459
|
-
if (append.status === "duplicate_consumed")
|
|
460
|
-
return null;
|
|
461
541
|
const inboxEnvelope = append.envelope;
|
|
462
542
|
if (append.status === "appended")
|
|
463
543
|
hostState.appendConversation(agent, {
|
|
@@ -1312,15 +1392,21 @@ function createHostShell({ env = process.env, runtimeHost, channelPackage, event
|
|
|
1312
1392
|
if (message.type === "agent-status") {
|
|
1313
1393
|
log("agent:status", message.agentId, message.status);
|
|
1314
1394
|
const observedAt = new Date().toISOString();
|
|
1315
|
-
if (message.status === "error" || message.status === "inactive")
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1395
|
+
if (message.status === "error" || message.status === "inactive") {
|
|
1396
|
+
if (message.readiness?.state && message.readiness.state !== "ready") {
|
|
1397
|
+
hostState.updateStatus(agent, { runtimeReadiness: { ...message.readiness, observedAt } });
|
|
1398
|
+
}
|
|
1399
|
+
else {
|
|
1400
|
+
projectFallbackRuntimeReadiness(agent, observedAt, message.status === "error" ? {
|
|
1401
|
+
state: "unavailable",
|
|
1402
|
+
reason: safeRuntimeStatusDiagnostic(message.error),
|
|
1403
|
+
nextAction: "Inspect the Runtime status error, correct the Runtime availability issue, then retry.",
|
|
1404
|
+
} : {
|
|
1405
|
+
state: "missing",
|
|
1406
|
+
reason: "Runtime is inactive",
|
|
1407
|
+
});
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1324
1410
|
else if (message.readiness) {
|
|
1325
1411
|
if (message.readiness.state === "ready" && unresolvedCurrentAuth(agent)) {
|
|
1326
1412
|
projectReadyUnlessUnresolvedAuth(agent, observedAt);
|
|
@@ -1338,7 +1424,7 @@ function createHostShell({ env = process.env, runtimeHost, channelPackage, event
|
|
|
1338
1424
|
redeliveryTimer.unref?.();
|
|
1339
1425
|
}
|
|
1340
1426
|
if (message.status === "error" && message.error)
|
|
1341
|
-
hostState.recordStatusError(agent, message.error);
|
|
1427
|
+
hostState.recordStatusError(agent, safeRuntimeStatusDiagnostic(message.error));
|
|
1342
1428
|
if (message.status === "error" || message.status === "inactive") {
|
|
1343
1429
|
processingEyes.clear(agent, `agent-status:${message.status}`);
|
|
1344
1430
|
}
|
|
@@ -1419,6 +1505,9 @@ function createHostShell({ env = process.env, runtimeHost, channelPackage, event
|
|
|
1419
1505
|
eventSourceStartTimer = null;
|
|
1420
1506
|
await Promise.resolve(eventSourceStop());
|
|
1421
1507
|
reminder.stopSync();
|
|
1508
|
+
if (auditRetryTimer)
|
|
1509
|
+
clearTimeout(auditRetryTimer);
|
|
1510
|
+
auditRetryTimer = null;
|
|
1422
1511
|
inboxAudit.stop();
|
|
1423
1512
|
interaction.stopSync();
|
|
1424
1513
|
await runtimeHost.shutdown(reason);
|
|
@@ -1637,6 +1726,8 @@ function createHostShell({ env = process.env, runtimeHost, channelPackage, event
|
|
|
1637
1726
|
previousSession: agentStates.get(agent.agentId)?.state.previousSessions?.[agent.runtime] ?? null,
|
|
1638
1727
|
})));
|
|
1639
1728
|
reminder.startSync();
|
|
1729
|
+
if (reconcileAuditRetries() > 0)
|
|
1730
|
+
scheduleAuditRetry();
|
|
1640
1731
|
inboxAudit.start();
|
|
1641
1732
|
interaction.startSync();
|
|
1642
1733
|
eventSourceStartTimer = setTimeout(() => { eventSourceStartTimer = null; startEventSource(); }, eventSourceStartDelayMs);
|
|
@@ -11,14 +11,15 @@ import { HostEnvelopeProjector, HostStateProjection, SenderIdentityCache, projec
|
|
|
11
11
|
import { ProcessingEyeOrchestrator } from "./host-processing-eye.mjs";
|
|
12
12
|
import { projectInboxEnvelope, targetKeyOfInboxEnvelope } from "../agent/inbox-projection.mjs";
|
|
13
13
|
import { HostReminderOrchestrator } from "../agent/host-reminder-orchestrator.mjs";
|
|
14
|
-
import { InboxAuditHeartbeat } from "../agent/inbox-audit-heartbeat.mjs";
|
|
15
|
-
import { inboxAuditRegistryFile, observeInboxAuditTarget } from "../agent/missed-outbound-scan.mjs";
|
|
14
|
+
import { boundedInboxAuditDiagnostic, InboxAuditHeartbeat, INBOX_AUDIT_CADENCE_MS } from "../agent/inbox-audit-heartbeat.mjs";
|
|
15
|
+
import { discardInboxAuditTargetRetry, enqueueInboxAuditTargetRetry, hasPendingInboxAuditTargets, inboxAuditRegistryFile, inboxAuditRetryFile, observeInboxAuditTarget, reconcileInboxAuditTargetRetries } from "../agent/missed-outbound-scan.mjs";
|
|
16
16
|
import { HostChannelBusiness } from "./host-channel-business.mjs";
|
|
17
17
|
import { HostInteractionOrchestrator } from "./interaction-orchestrator.mjs";
|
|
18
18
|
import { targetFor } from "./message-policy.mjs";
|
|
19
19
|
import { authFailureAppliesTo, parsePersistedAuthFailure, providerAuthenticationFailureReadiness, readinessForPersistedAuthFailure, RuntimePrerequisiteError, } from "../runtime/runtime-readiness.mjs";
|
|
20
|
+
import { safeProviderDiagnostic } from "../runtime/provider-error-classifier.mjs";
|
|
20
21
|
import { readDocumentCommentSubscription, verifyCallbackProbe } from "../platform/callback-capability.mjs";
|
|
21
|
-
import { loadConfig, resolveMentionPolicy } from "../platform/config.mjs";
|
|
22
|
+
import { loadConfig, resolveInboxAuditSchedule, resolveMentionPolicy } from "../platform/config.mjs";
|
|
22
23
|
import { processCommandToken } from "../app/internal-command.mjs";
|
|
23
24
|
import { managedOfficialLarkCli } from "../app/agent-lark-cli-workspace.mjs";
|
|
24
25
|
import { isChannelReconnecting, isRuntimeReadinessCurrent } from "../app/agent-readiness.mjs";
|
|
@@ -79,6 +80,9 @@ export function memberNamesFromPayloads(payloads) {
|
|
|
79
80
|
return names;
|
|
80
81
|
}
|
|
81
82
|
function errorMessage(error) { return error instanceof Error ? error.message : String(error); }
|
|
83
|
+
function safeRuntimeStatusDiagnostic(error) {
|
|
84
|
+
return safeProviderDiagnostic(error, "Runtime entered an error state", 500);
|
|
85
|
+
}
|
|
82
86
|
function agentConfigSignature(agent) {
|
|
83
87
|
return JSON.stringify({
|
|
84
88
|
agentId: agent.agentId, runtime: agent.runtime, model: agent.model, effort: agent.effort ?? null,
|
|
@@ -193,17 +197,40 @@ export function createHostShell({ env = process.env, runtimeHost, channelPackage
|
|
|
193
197
|
}
|
|
194
198
|
hostState.updateStatus(agent, { runtimeReadiness: { runtime: agent.runtime, state: "ready", observedAt } });
|
|
195
199
|
};
|
|
200
|
+
const projectFallbackRuntimeReadiness = (agent, observedAt, fallback, preserveCurrentUnavailableDiagnostic = false) => {
|
|
201
|
+
const failure = unresolvedCurrentAuth(agent);
|
|
202
|
+
if (failure) {
|
|
203
|
+
hostState.updateStatus(agent, {
|
|
204
|
+
runtimeReadiness: { ...readinessForPersistedAuthFailure(failure), observedAt },
|
|
205
|
+
});
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
const currentReadiness = hostState.readStatus(agent).runtimeReadiness;
|
|
209
|
+
const observedMs = Date.parse(String(currentReadiness?.observedAt || ""));
|
|
210
|
+
const daemonStartedMs = Date.parse(daemonStartedAt);
|
|
211
|
+
const currentEpoch = currentReadiness?.runtime === agent.runtime
|
|
212
|
+
&& Number.isFinite(observedMs) && Number.isFinite(daemonStartedMs) && observedMs >= daemonStartedMs;
|
|
213
|
+
if (currentEpoch && (currentReadiness?.state === "missing" || currentReadiness?.state === "unauthenticated" || currentReadiness?.state === "incompatible"))
|
|
214
|
+
return;
|
|
215
|
+
if (currentEpoch && preserveCurrentUnavailableDiagnostic && currentReadiness?.state === "unavailable"
|
|
216
|
+
&& typeof currentReadiness.reason === "string" && currentReadiness.reason.trim()) {
|
|
217
|
+
hostState.updateStatus(agent, {
|
|
218
|
+
runtimeReadiness: { ...currentReadiness, runtime: agent.runtime, state: "unavailable", observedAt },
|
|
219
|
+
});
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
hostState.updateStatus(agent, {
|
|
223
|
+
runtimeReadiness: { runtime: agent.runtime, ...fallback, observedAt },
|
|
224
|
+
});
|
|
225
|
+
};
|
|
196
226
|
const recordInboundDeliveryFailure = (agent, code) => {
|
|
197
227
|
const at = new Date().toISOString();
|
|
198
228
|
const reason = "Inbound Runtime delivery failed non-retryably; durable Inbox/ledger state is retained for recovery.";
|
|
199
229
|
const nextAction = "Inspect the delivery/status error, correct the Runtime or canonical Inbox state, then restart to replay safely.";
|
|
200
|
-
const currentReadiness = hostState.readStatus(agent).runtimeReadiness;
|
|
201
230
|
hostState.updateStatus(agent, {
|
|
202
231
|
inboundDeliveryHealth: { state: "error", code, at, reason, nextAction },
|
|
203
|
-
...(currentReadiness?.state === "unauthenticated" ? {} : {
|
|
204
|
-
runtimeReadiness: { runtime: agent.runtime, state: "incompatible", reason, nextAction },
|
|
205
|
-
}),
|
|
206
232
|
});
|
|
233
|
+
projectFallbackRuntimeReadiness(agent, at, { state: "unavailable", reason, nextAction }, true);
|
|
207
234
|
hostState.recordStatusError(agent, `${reason} ${nextAction} code=${code}`);
|
|
208
235
|
};
|
|
209
236
|
const agentStates = new Map();
|
|
@@ -357,13 +384,57 @@ export function createHostShell({ env = process.env, runtimeHost, channelPackage
|
|
|
357
384
|
for (const agent of agents)
|
|
358
385
|
prepareAgentState(agent);
|
|
359
386
|
const reminder = new HostReminderOrchestrator({ agents, stateStore, envelopeProjector, deliveryTarget: runtimeHost, log });
|
|
387
|
+
const auditRegistry = inboxAuditRegistryFile(larkinHome);
|
|
388
|
+
const auditRetryJournal = inboxAuditRetryFile(larkinHome);
|
|
389
|
+
let auditRetryTimer = null;
|
|
390
|
+
let auditRetryAttempt = 0;
|
|
391
|
+
const reconcileAuditRetries = () => {
|
|
392
|
+
try {
|
|
393
|
+
return reconcileInboxAuditTargetRetries(auditRegistry, auditRetryJournal).remaining;
|
|
394
|
+
}
|
|
395
|
+
catch (error) {
|
|
396
|
+
log(`inbox audit retry 读取失败: ${boundedInboxAuditDiagnostic(error)}`);
|
|
397
|
+
return 1;
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
const scheduleAuditRetry = () => {
|
|
401
|
+
if (auditRetryTimer || auditRetryAttempt >= 5)
|
|
402
|
+
return;
|
|
403
|
+
const delay = [100, 250, 1_000, 3_000, 10_000][auditRetryAttempt++];
|
|
404
|
+
auditRetryTimer = setTimeout(() => {
|
|
405
|
+
auditRetryTimer = null;
|
|
406
|
+
if (reconcileAuditRetries() > 0)
|
|
407
|
+
scheduleAuditRetry();
|
|
408
|
+
else
|
|
409
|
+
auditRetryAttempt = 0;
|
|
410
|
+
}, delay);
|
|
411
|
+
auditRetryTimer.unref?.();
|
|
412
|
+
};
|
|
360
413
|
const inboxAudit = new InboxAuditHeartbeat({
|
|
361
414
|
agents,
|
|
362
415
|
stateStore,
|
|
363
416
|
runtimeHost,
|
|
364
417
|
log,
|
|
418
|
+
configFile: path.join(larkinHome, "config.json"),
|
|
419
|
+
schedule(agent) {
|
|
420
|
+
try {
|
|
421
|
+
return resolveInboxAuditSchedule(loadConfig(env).config, agent.agentId);
|
|
422
|
+
}
|
|
423
|
+
catch (error) {
|
|
424
|
+
log(`inbox audit schedule 读取失败 agent=${agent.agentId}: ${errorMessage(error)}`);
|
|
425
|
+
return { enabled: false, intervalMs: INBOX_AUDIT_CADENCE_MS };
|
|
426
|
+
}
|
|
427
|
+
},
|
|
428
|
+
shouldDispatch(agent) {
|
|
429
|
+
try {
|
|
430
|
+
return hasPendingInboxAuditTargets(auditRegistry, agent.agentId);
|
|
431
|
+
}
|
|
432
|
+
catch (error) {
|
|
433
|
+
log(`inbox audit pending 读取失败 agent=${agent.agentId}: ${errorMessage(error)}`);
|
|
434
|
+
return false;
|
|
435
|
+
}
|
|
436
|
+
},
|
|
365
437
|
});
|
|
366
|
-
const auditRegistry = inboxAuditRegistryFile(larkinHome);
|
|
367
438
|
const seenEventIds = new Set();
|
|
368
439
|
const inFlightEventIds = new Set();
|
|
369
440
|
const onFeishuMessage = async (agent, event, options) => {
|
|
@@ -410,14 +481,23 @@ export function createHostShell({ env = process.env, runtimeHost, channelPackage
|
|
|
410
481
|
// append/dedupe decision is durable. Agent model-seen state is untouched.
|
|
411
482
|
if (event.event_id)
|
|
412
483
|
seenEventIds.add(eventKey);
|
|
484
|
+
if (append.status === "duplicate_consumed")
|
|
485
|
+
return null;
|
|
486
|
+
const auditSourceSeq = Number(append.envelope.target_seq);
|
|
413
487
|
try {
|
|
414
|
-
observeInboxAuditTarget(auditRegistry, agent.agentId, event);
|
|
488
|
+
observeInboxAuditTarget(auditRegistry, agent.agentId, { ...event, wake, source_seq: auditSourceSeq });
|
|
489
|
+
discardInboxAuditTargetRetry(auditRetryJournal, agent.agentId, { ...event, source_seq: auditSourceSeq });
|
|
415
490
|
}
|
|
416
491
|
catch (error) {
|
|
417
|
-
|
|
492
|
+
try {
|
|
493
|
+
enqueueInboxAuditTargetRetry(auditRetryJournal, agent.agentId, { ...event, wake, source_seq: auditSourceSeq });
|
|
494
|
+
scheduleAuditRetry();
|
|
495
|
+
log(`inbox audit target 延后重试: ${boundedInboxAuditDiagnostic(error)}`);
|
|
496
|
+
}
|
|
497
|
+
catch (retryError) {
|
|
498
|
+
log(`inbox audit target 未持久化: ${boundedInboxAuditDiagnostic(retryError)}`);
|
|
499
|
+
}
|
|
418
500
|
}
|
|
419
|
-
if (append.status === "duplicate_consumed")
|
|
420
|
-
return null;
|
|
421
501
|
const inboxEnvelope = append.envelope;
|
|
422
502
|
if (append.status === "appended")
|
|
423
503
|
hostState.appendConversation(agent, {
|
|
@@ -1272,15 +1352,21 @@ export function createHostShell({ env = process.env, runtimeHost, channelPackage
|
|
|
1272
1352
|
if (message.type === "agent-status") {
|
|
1273
1353
|
log("agent:status", message.agentId, message.status);
|
|
1274
1354
|
const observedAt = new Date().toISOString();
|
|
1275
|
-
if (message.status === "error" || message.status === "inactive")
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1355
|
+
if (message.status === "error" || message.status === "inactive") {
|
|
1356
|
+
if (message.readiness?.state && message.readiness.state !== "ready") {
|
|
1357
|
+
hostState.updateStatus(agent, { runtimeReadiness: { ...message.readiness, observedAt } });
|
|
1358
|
+
}
|
|
1359
|
+
else {
|
|
1360
|
+
projectFallbackRuntimeReadiness(agent, observedAt, message.status === "error" ? {
|
|
1361
|
+
state: "unavailable",
|
|
1362
|
+
reason: safeRuntimeStatusDiagnostic(message.error),
|
|
1363
|
+
nextAction: "Inspect the Runtime status error, correct the Runtime availability issue, then retry.",
|
|
1364
|
+
} : {
|
|
1365
|
+
state: "missing",
|
|
1366
|
+
reason: "Runtime is inactive",
|
|
1367
|
+
});
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1284
1370
|
else if (message.readiness) {
|
|
1285
1371
|
if (message.readiness.state === "ready" && unresolvedCurrentAuth(agent)) {
|
|
1286
1372
|
projectReadyUnlessUnresolvedAuth(agent, observedAt);
|
|
@@ -1298,7 +1384,7 @@ export function createHostShell({ env = process.env, runtimeHost, channelPackage
|
|
|
1298
1384
|
redeliveryTimer.unref?.();
|
|
1299
1385
|
}
|
|
1300
1386
|
if (message.status === "error" && message.error)
|
|
1301
|
-
hostState.recordStatusError(agent, message.error);
|
|
1387
|
+
hostState.recordStatusError(agent, safeRuntimeStatusDiagnostic(message.error));
|
|
1302
1388
|
if (message.status === "error" || message.status === "inactive") {
|
|
1303
1389
|
processingEyes.clear(agent, `agent-status:${message.status}`);
|
|
1304
1390
|
}
|
|
@@ -1379,6 +1465,9 @@ export function createHostShell({ env = process.env, runtimeHost, channelPackage
|
|
|
1379
1465
|
eventSourceStartTimer = null;
|
|
1380
1466
|
await Promise.resolve(eventSourceStop());
|
|
1381
1467
|
reminder.stopSync();
|
|
1468
|
+
if (auditRetryTimer)
|
|
1469
|
+
clearTimeout(auditRetryTimer);
|
|
1470
|
+
auditRetryTimer = null;
|
|
1382
1471
|
inboxAudit.stop();
|
|
1383
1472
|
interaction.stopSync();
|
|
1384
1473
|
await runtimeHost.shutdown(reason);
|
|
@@ -1597,6 +1686,8 @@ export function createHostShell({ env = process.env, runtimeHost, channelPackage
|
|
|
1597
1686
|
previousSession: agentStates.get(agent.agentId)?.state.previousSessions?.[agent.runtime] ?? null,
|
|
1598
1687
|
})));
|
|
1599
1688
|
reminder.startSync();
|
|
1689
|
+
if (reconcileAuditRetries() > 0)
|
|
1690
|
+
scheduleAuditRetry();
|
|
1600
1691
|
inboxAudit.start();
|
|
1601
1692
|
interaction.startSync();
|
|
1602
1693
|
eventSourceStartTimer = setTimeout(() => { eventSourceStartTimer = null; startEventSource(); }, eventSourceStartDelayMs);
|