@alook/daemon 0.1.18 → 0.1.20
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/cli/index.js +254 -37
- package/dist/index.js +2330 -93
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -17679,7 +17679,7 @@ var AuditLogWakeTriggerPayloadSchema = exports_external.object({
|
|
|
17679
17679
|
reason: exports_external.enum(["unread", "mention"])
|
|
17680
17680
|
});
|
|
17681
17681
|
var AuditLogSessionResetPayloadSchema = exports_external.object({
|
|
17682
|
-
trigger: exports_external.enum(["single", "reset_all"])
|
|
17682
|
+
trigger: exports_external.enum(["single", "reset_all", "idle_timeout"])
|
|
17683
17683
|
});
|
|
17684
17684
|
var AuditLogNapPayloadSchema = exports_external.object({
|
|
17685
17685
|
trigger: exports_external.literal("nap")
|
|
@@ -17722,10 +17722,33 @@ var BotAuditEventKindSchema = exports_external.enum([
|
|
|
17722
17722
|
]);
|
|
17723
17723
|
var HostBotAuditEventFrameSchema = exports_external.object({
|
|
17724
17724
|
type: exports_external.literal("bot_audit_event"),
|
|
17725
|
+
eventId: exports_external.string().min(1).max(128).optional(),
|
|
17726
|
+
occurredAt: exports_external.string().datetime({ offset: true }).optional(),
|
|
17725
17727
|
agentId: exports_external.string().min(1),
|
|
17726
17728
|
sessionId: exports_external.string().nullable().optional(),
|
|
17727
17729
|
launchId: exports_external.string().nullable().optional(),
|
|
17728
17730
|
event: BotAuditEventSchema
|
|
17731
|
+
}).superRefine((frame, ctx) => {
|
|
17732
|
+
if (frame.event.kind === "session_reset" && frame.event.payload.trigger === "idle_timeout" && (!frame.eventId || !frame.occurredAt)) {
|
|
17733
|
+
if (!frame.eventId) {
|
|
17734
|
+
ctx.addIssue({
|
|
17735
|
+
code: "custom",
|
|
17736
|
+
path: ["eventId"],
|
|
17737
|
+
message: "eventId is required for idle_timeout session_reset"
|
|
17738
|
+
});
|
|
17739
|
+
}
|
|
17740
|
+
if (!frame.occurredAt) {
|
|
17741
|
+
ctx.addIssue({
|
|
17742
|
+
code: "custom",
|
|
17743
|
+
path: ["occurredAt"],
|
|
17744
|
+
message: "occurredAt is required for idle_timeout session_reset"
|
|
17745
|
+
});
|
|
17746
|
+
}
|
|
17747
|
+
}
|
|
17748
|
+
});
|
|
17749
|
+
var BotAuditEventAckFrameSchema = exports_external.strictObject({
|
|
17750
|
+
type: exports_external.literal("bot_audit_event_ack"),
|
|
17751
|
+
eventId: exports_external.string().min(1).max(128)
|
|
17729
17752
|
});
|
|
17730
17753
|
// ../shared/src/db/community-schema.ts
|
|
17731
17754
|
var exports_community_schema = {};
|
|
@@ -17736,6 +17759,7 @@ __export(exports_community_schema, {
|
|
|
17736
17759
|
communityServerFolderItem: () => communityServerFolderItem,
|
|
17737
17760
|
communityServerFolder: () => communityServerFolder,
|
|
17738
17761
|
communityServer: () => communityServer,
|
|
17762
|
+
communityReadStateRevision: () => communityReadStateRevision,
|
|
17739
17763
|
communityReadState: () => communityReadState,
|
|
17740
17764
|
communityReaction: () => communityReaction,
|
|
17741
17765
|
communityPin: () => communityPin,
|
|
@@ -17900,6 +17924,10 @@ var communityReadState = sqliteTable("community_read_state", {
|
|
|
17900
17924
|
lastReadMessageId: text("last_read_message_id"),
|
|
17901
17925
|
lastReadSeq: integer2("last_read_seq").notNull().default(0)
|
|
17902
17926
|
}, (t) => [index("idx_read_state_user").on(t.userId)]);
|
|
17927
|
+
var communityReadStateRevision = sqliteTable("community_read_state_revision", {
|
|
17928
|
+
userId: text("user_id").primaryKey().references(() => user.id, { onDelete: "cascade" }),
|
|
17929
|
+
revision: integer2("revision").notNull().default(0)
|
|
17930
|
+
});
|
|
17903
17931
|
var communityReaction = sqliteTable("community_reaction", {
|
|
17904
17932
|
id: text("id").primaryKey().$defaultFn(() => nanoid3()),
|
|
17905
17933
|
messageId: text("message_id").notNull().references(() => communityMessage.id, { onDelete: "cascade" }),
|
|
@@ -18493,6 +18521,24 @@ var communityUnreadBumpSchema = exports_external.strictObject({
|
|
|
18493
18521
|
railChannelId: string4.optional(),
|
|
18494
18522
|
isMention: exports_external.boolean().optional()
|
|
18495
18523
|
});
|
|
18524
|
+
var readStateEnvelopeFields = {
|
|
18525
|
+
revision: exports_external.number().int().positive(),
|
|
18526
|
+
inboxChanged: exports_external.literal(true)
|
|
18527
|
+
};
|
|
18528
|
+
var communityReadStateAdvancedSchema = exports_external.strictObject({
|
|
18529
|
+
type: exports_external.literal("community:read_state.advanced"),
|
|
18530
|
+
...readStateEnvelopeFields
|
|
18531
|
+
});
|
|
18532
|
+
var communityInboxChangedSchema = exports_external.strictObject({
|
|
18533
|
+
type: exports_external.literal("community:inbox.changed"),
|
|
18534
|
+
...readStateEnvelopeFields,
|
|
18535
|
+
reason: exports_external.enum([
|
|
18536
|
+
"read_all",
|
|
18537
|
+
"mention_read_all",
|
|
18538
|
+
"mention_dismiss",
|
|
18539
|
+
"notification_policy"
|
|
18540
|
+
])
|
|
18541
|
+
});
|
|
18496
18542
|
var communityPresenceUpdateSchema = exports_external.strictObject({
|
|
18497
18543
|
type: exports_external.literal("community:presence.update"),
|
|
18498
18544
|
userId: string4,
|
|
@@ -18588,6 +18634,8 @@ var CommunityWsEventDiscriminatedSchema = exports_external.discriminatedUnion("t
|
|
|
18588
18634
|
communityInviteCreateSchema,
|
|
18589
18635
|
communityMentionCreateSchema,
|
|
18590
18636
|
communityUnreadBumpSchema,
|
|
18637
|
+
communityReadStateAdvancedSchema,
|
|
18638
|
+
communityInboxChangedSchema,
|
|
18591
18639
|
communityPresenceUpdateSchema,
|
|
18592
18640
|
communityStatusUpdateSchema,
|
|
18593
18641
|
communityMachineCreatedSchema,
|
|
@@ -18632,6 +18680,8 @@ var WS_EVENTS = {
|
|
|
18632
18680
|
INVITE_CREATE: "community:invite.create",
|
|
18633
18681
|
MENTION_CREATE: "community:mention.create",
|
|
18634
18682
|
UNREAD_BUMP: "community:unread.bump",
|
|
18683
|
+
READ_STATE_ADVANCED: "community:read_state.advanced",
|
|
18684
|
+
INBOX_CHANGED: "community:inbox.changed",
|
|
18635
18685
|
PRESENCE_UPDATE: "community:presence.update",
|
|
18636
18686
|
STATUS_UPDATE: "community:status.update",
|
|
18637
18687
|
MACHINE_CREATED: "community:machine.created",
|
|
@@ -25681,6 +25731,7 @@ var DEFAULT_PING_INTERVAL_MS = 15000;
|
|
|
25681
25731
|
var DEFAULT_PONG_TIMEOUT_MS = 30000;
|
|
25682
25732
|
var DEFAULT_RECONNECT_BASE_MS = 500;
|
|
25683
25733
|
var DEFAULT_RECONNECT_MAX_MS = 30000;
|
|
25734
|
+
var DEFAULT_AUDIT_ACK_RETRY_MS = 5000;
|
|
25684
25735
|
function describeErr(err) {
|
|
25685
25736
|
return err instanceof Error ? err.message : String(err);
|
|
25686
25737
|
}
|
|
@@ -25696,8 +25747,10 @@ class WsControlChannel {
|
|
|
25696
25747
|
closedByUser = false;
|
|
25697
25748
|
authRejected = false;
|
|
25698
25749
|
pingTimer = null;
|
|
25750
|
+
auditRetryTimer = null;
|
|
25699
25751
|
pongDeadline = 0;
|
|
25700
25752
|
resyncProvider = null;
|
|
25753
|
+
pendingBotAuditEvents = new Map;
|
|
25701
25754
|
log;
|
|
25702
25755
|
wakeCoordinator = new WakeCoordinator;
|
|
25703
25756
|
constructor(opts) {
|
|
@@ -25715,6 +25768,7 @@ class WsControlChannel {
|
|
|
25715
25768
|
close() {
|
|
25716
25769
|
this.closedByUser = true;
|
|
25717
25770
|
this.clearHeartbeat();
|
|
25771
|
+
this.clearAuditRetry();
|
|
25718
25772
|
this.ws?.close();
|
|
25719
25773
|
this.ws = null;
|
|
25720
25774
|
this.statusValue = "closed";
|
|
@@ -25772,7 +25826,26 @@ class WsControlChannel {
|
|
|
25772
25826
|
this.sendFrame({ type: "agent_typing_stop", ...info });
|
|
25773
25827
|
}
|
|
25774
25828
|
async reportBotAuditEvent(frame) {
|
|
25829
|
+
if (frame.event.kind === "session_reset" && frame.event.payload.trigger === "idle_timeout") {
|
|
25830
|
+
if (!frame.eventId || !frame.occurredAt) {
|
|
25831
|
+
this.log.error("durable idle-reset audit missing local receipt", {
|
|
25832
|
+
agentId: frame.agentId
|
|
25833
|
+
});
|
|
25834
|
+
return;
|
|
25835
|
+
}
|
|
25836
|
+
this.pendingBotAuditEvents.set(frame.eventId, frame);
|
|
25837
|
+
this.sendFrame(frame);
|
|
25838
|
+
this.scheduleAuditRetry();
|
|
25839
|
+
return;
|
|
25840
|
+
}
|
|
25841
|
+
this.sendFrame(frame);
|
|
25842
|
+
}
|
|
25843
|
+
restorePendingBotAuditEvent(frame) {
|
|
25844
|
+
if (!frame.eventId || !frame.occurredAt)
|
|
25845
|
+
return;
|
|
25846
|
+
this.pendingBotAuditEvents.set(frame.eventId, frame);
|
|
25775
25847
|
this.sendFrame(frame);
|
|
25848
|
+
this.scheduleAuditRetry();
|
|
25776
25849
|
}
|
|
25777
25850
|
async reportWakeAck(info) {
|
|
25778
25851
|
this.wakeCoordinator.recordDeliveryAck(info.agentId, info.launchId, info.status);
|
|
@@ -25800,10 +25873,14 @@ class WsControlChannel {
|
|
|
25800
25873
|
const liveActivities = activities ?? [];
|
|
25801
25874
|
for (const a of liveActivities)
|
|
25802
25875
|
this.sendFrame({ type: "agent_activity", ...a });
|
|
25876
|
+
for (const frame of this.pendingBotAuditEvents.values())
|
|
25877
|
+
this.sendFrame(frame);
|
|
25878
|
+
this.scheduleAuditRetry();
|
|
25803
25879
|
this.log.info("resync sent", {
|
|
25804
25880
|
ready: ready.runtimeReport.length,
|
|
25805
25881
|
sessions: sessions.length,
|
|
25806
|
-
activities: liveActivities.length
|
|
25882
|
+
activities: liveActivities.length,
|
|
25883
|
+
pendingAuditEvents: this.pendingBotAuditEvents.size
|
|
25807
25884
|
});
|
|
25808
25885
|
}
|
|
25809
25886
|
for (const hook of this.resyncHooks) {
|
|
@@ -25846,6 +25923,34 @@ class WsControlChannel {
|
|
|
25846
25923
|
this.opts.onAuthRejected?.();
|
|
25847
25924
|
return;
|
|
25848
25925
|
}
|
|
25926
|
+
const auditAck = BotAuditEventAckFrameSchema.safeParse(frame);
|
|
25927
|
+
if (auditAck.success) {
|
|
25928
|
+
this.attempt = 0;
|
|
25929
|
+
const pending = this.pendingBotAuditEvents.get(auditAck.data.eventId);
|
|
25930
|
+
if (!pending)
|
|
25931
|
+
return;
|
|
25932
|
+
let durableCleared = this.opts.onBotAuditEventAck === undefined;
|
|
25933
|
+
try {
|
|
25934
|
+
durableCleared = this.opts.onBotAuditEventAck?.({
|
|
25935
|
+
agentId: pending.agentId,
|
|
25936
|
+
eventId: auditAck.data.eventId
|
|
25937
|
+
}) ?? true;
|
|
25938
|
+
} catch (err) {
|
|
25939
|
+
this.log.warn("bot audit ack local clear threw", {
|
|
25940
|
+
eventId: auditAck.data.eventId,
|
|
25941
|
+
err: describeErr(err)
|
|
25942
|
+
});
|
|
25943
|
+
}
|
|
25944
|
+
if (durableCleared)
|
|
25945
|
+
this.pendingBotAuditEvents.delete(auditAck.data.eventId);
|
|
25946
|
+
else
|
|
25947
|
+
this.log.warn("bot audit ack local clear deferred", { eventId: auditAck.data.eventId });
|
|
25948
|
+
if (this.pendingBotAuditEvents.size === 0)
|
|
25949
|
+
this.clearAuditRetry();
|
|
25950
|
+
else
|
|
25951
|
+
this.scheduleAuditRetry();
|
|
25952
|
+
return;
|
|
25953
|
+
}
|
|
25849
25954
|
this.attempt = 0;
|
|
25850
25955
|
this.ingestCommand(frame).catch((err) => {
|
|
25851
25956
|
this.log.warn("command ingress failed", { type: frame.type, err: describeErr(err) });
|
|
@@ -25920,6 +26025,7 @@ class WsControlChannel {
|
|
|
25920
26025
|
onSocketClosed(code, reason) {
|
|
25921
26026
|
this.log.warn("control channel closed", { code, reason: reason ? String(reason) : "" });
|
|
25922
26027
|
this.clearHeartbeat();
|
|
26028
|
+
this.clearAuditRetry();
|
|
25923
26029
|
this.ws = null;
|
|
25924
26030
|
if (this.closedByUser)
|
|
25925
26031
|
return;
|
|
@@ -25967,6 +26073,26 @@ class WsControlChannel {
|
|
|
25967
26073
|
this.pingTimer = null;
|
|
25968
26074
|
}
|
|
25969
26075
|
}
|
|
26076
|
+
scheduleAuditRetry() {
|
|
26077
|
+
if (this.auditRetryTimer || this.pendingBotAuditEvents.size === 0)
|
|
26078
|
+
return;
|
|
26079
|
+
const delayMs = this.opts.auditAckRetryMs ?? DEFAULT_AUDIT_ACK_RETRY_MS;
|
|
26080
|
+
this.auditRetryTimer = setTimeout(() => {
|
|
26081
|
+
this.auditRetryTimer = null;
|
|
26082
|
+
if (this.statusValue === "open") {
|
|
26083
|
+
for (const frame of this.pendingBotAuditEvents.values())
|
|
26084
|
+
this.sendFrame(frame);
|
|
26085
|
+
}
|
|
26086
|
+
this.scheduleAuditRetry();
|
|
26087
|
+
}, delayMs);
|
|
26088
|
+
this.auditRetryTimer.unref?.();
|
|
26089
|
+
}
|
|
26090
|
+
clearAuditRetry() {
|
|
26091
|
+
if (!this.auditRetryTimer)
|
|
26092
|
+
return;
|
|
26093
|
+
clearTimeout(this.auditRetryTimer);
|
|
26094
|
+
this.auditRetryTimer = null;
|
|
26095
|
+
}
|
|
25970
26096
|
now() {
|
|
25971
26097
|
return this.opts.now ? this.opts.now() : Date.now();
|
|
25972
26098
|
}
|
|
@@ -27670,14 +27796,14 @@ class AgentProcessManager {
|
|
|
27670
27796
|
const effects = this.dispatch({ type: "wake", agentId, message: normalized, nowMs: this.now() });
|
|
27671
27797
|
return effects.length > 0;
|
|
27672
27798
|
}
|
|
27673
|
-
forgetSession(agentId, barrierType = "reset_session", forgottenSessionId) {
|
|
27674
|
-
if (!this.forgetSessionSources(agentId, barrierType, forgottenSessionId))
|
|
27799
|
+
forgetSession(agentId, barrierType = "reset_session", forgottenSessionId, pendingIdleResetEvent) {
|
|
27800
|
+
if (!this.forgetSessionSources(agentId, barrierType, forgottenSessionId, pendingIdleResetEvent))
|
|
27675
27801
|
return false;
|
|
27676
27802
|
this.dispatch({ type: "reset_session", agentId });
|
|
27677
27803
|
return true;
|
|
27678
27804
|
}
|
|
27679
|
-
forgetSessionSources(agentId, barrierType, forgottenSessionId) {
|
|
27680
|
-
const persisted = this.opts.timeline?.forgetSession(agentId, barrierType, forgottenSessionId);
|
|
27805
|
+
forgetSessionSources(agentId, barrierType, forgottenSessionId, pendingIdleResetEvent) {
|
|
27806
|
+
const persisted = pendingIdleResetEvent ? this.opts.timeline?.forgetSession(agentId, barrierType, forgottenSessionId, pendingIdleResetEvent) : this.opts.timeline?.forgetSession(agentId, barrierType, forgottenSessionId);
|
|
27681
27807
|
if (persisted === false)
|
|
27682
27808
|
return false;
|
|
27683
27809
|
this.resumeSessions.delete(agentId);
|
|
@@ -27781,6 +27907,9 @@ class AgentProcessManager {
|
|
|
27781
27907
|
snapshot() {
|
|
27782
27908
|
return this.state;
|
|
27783
27909
|
}
|
|
27910
|
+
runningAgentCount() {
|
|
27911
|
+
return this.sessions.size;
|
|
27912
|
+
}
|
|
27784
27913
|
auditContext(agentId) {
|
|
27785
27914
|
return {
|
|
27786
27915
|
sessionId: this.liveSessions.get(agentId) ?? null,
|
|
@@ -28253,7 +28382,11 @@ ${this.opts.wakePromptFooter}` : text2;
|
|
|
28253
28382
|
break;
|
|
28254
28383
|
case "reset_idle_session": {
|
|
28255
28384
|
const spawnState = this.activeSpawnState.get(effect.agentId);
|
|
28256
|
-
const
|
|
28385
|
+
const completion = {
|
|
28386
|
+
eventId: `bae_${randomUUID5()}`,
|
|
28387
|
+
occurredAt: new Date(this.now()).toISOString()
|
|
28388
|
+
};
|
|
28389
|
+
const persisted = this.forgetSession(effect.agentId, "reset_session", effect.sessionId, completion);
|
|
28257
28390
|
if (!persisted) {
|
|
28258
28391
|
this.log.error("idle session reset barrier was not persisted; reset deferred", {
|
|
28259
28392
|
agentId: effect.agentId,
|
|
@@ -28265,6 +28398,23 @@ ${this.opts.wakePromptFooter}` : text2;
|
|
|
28265
28398
|
if (spawnState)
|
|
28266
28399
|
spawnState.discardEvents = true;
|
|
28267
28400
|
this.dispatch({ type: "idle_reset_committed", agentId: effect.agentId, nowMs: this.now() });
|
|
28401
|
+
if (this.opts.onBotAuditEvent) {
|
|
28402
|
+
try {
|
|
28403
|
+
this.opts.onBotAuditEvent(effect.agentId, {
|
|
28404
|
+
kind: "session_reset",
|
|
28405
|
+
payload: { trigger: "idle_timeout" }
|
|
28406
|
+
}, {
|
|
28407
|
+
sessionId: null,
|
|
28408
|
+
launchId: null,
|
|
28409
|
+
...completion
|
|
28410
|
+
});
|
|
28411
|
+
} catch (err) {
|
|
28412
|
+
this.log.debug("audit emit failed (idle session reset)", {
|
|
28413
|
+
agentId: effect.agentId,
|
|
28414
|
+
err: String(err)
|
|
28415
|
+
});
|
|
28416
|
+
}
|
|
28417
|
+
}
|
|
28268
28418
|
this.log.info("idle agent session reset", {
|
|
28269
28419
|
agentId: effect.agentId,
|
|
28270
28420
|
sessionId: effect.sessionId
|
|
@@ -28902,6 +29052,7 @@ Then read @memory.md and your .context_timeline for durable context, and pull `
|
|
|
28902
29052
|
class AgentRouter {
|
|
28903
29053
|
opts;
|
|
28904
29054
|
running = new Set;
|
|
29055
|
+
nextWakeAdmissionOrdinal = 1;
|
|
28905
29056
|
runtimes = new Map;
|
|
28906
29057
|
pendingResend = false;
|
|
28907
29058
|
scheduleResend;
|
|
@@ -29062,7 +29213,7 @@ class AgentRouter {
|
|
|
29062
29213
|
this.opts.typingTracker?.add(cmd.agentId, channelScope);
|
|
29063
29214
|
const text2 = (this.opts.formatUnreadNoticeText ?? defaultFormatUnreadNoticeText)(cmd.unreadNotice);
|
|
29064
29215
|
const producedEffect = this.opts.manager.deliver(cmd.agentId, {
|
|
29065
|
-
id: `${cmd.agentId}:wake:${cmd.unreadNotice.channel}:${cmd.unreadNotice.latestSeq}`,
|
|
29216
|
+
id: `${cmd.agentId}:wake:${cmd.unreadNotice.channel}:${cmd.unreadNotice.latestSeq}:admission:${this.nextWakeAdmissionOrdinal++}`,
|
|
29066
29217
|
seq: cmd.unreadNotice.latestSeq,
|
|
29067
29218
|
text: text2
|
|
29068
29219
|
});
|
|
@@ -29286,11 +29437,13 @@ var TIMELINE_READ_CHUNK_BYTES = 65536;
|
|
|
29286
29437
|
var DATE_FILENAME_PATTERN = /^\d{4}-\d{2}-\d{2}\.jsonl$/;
|
|
29287
29438
|
var RESUME_CONTROL_FILENAME = ".resume-control.json";
|
|
29288
29439
|
var RESUME_CONTROL_MAX_BYTES = 4096;
|
|
29440
|
+
var MAX_PENDING_IDLE_RESET_EVENTS = 32;
|
|
29289
29441
|
var EMPTY_RESUME_CONTROL = {
|
|
29290
29442
|
version: 1,
|
|
29291
29443
|
attemptedSessionId: null,
|
|
29292
29444
|
fencedSessionId: null,
|
|
29293
|
-
fullBarrier: null
|
|
29445
|
+
fullBarrier: null,
|
|
29446
|
+
pendingIdleResetEvents: []
|
|
29294
29447
|
};
|
|
29295
29448
|
function isBarrier(entry) {
|
|
29296
29449
|
return entry.system !== undefined;
|
|
@@ -29680,7 +29833,8 @@ function readResumeControlState(timelineDir) {
|
|
|
29680
29833
|
const raw = bounded.subarray(0, bytesRead).toString("utf8");
|
|
29681
29834
|
const value = JSON.parse(raw);
|
|
29682
29835
|
const validSessionId = (candidate) => candidate === null || typeof candidate === "string" && candidate.length > 0 && candidate.length <= 512;
|
|
29683
|
-
|
|
29836
|
+
const pendingIdleResetEvents = value.pendingIdleResetEvents === undefined ? [] : value.pendingIdleResetEvents;
|
|
29837
|
+
if (value.version !== 1 || !validSessionId(value.attemptedSessionId) || !validSessionId(value.fencedSessionId) || !Array.isArray(pendingIdleResetEvents) || pendingIdleResetEvents.length > MAX_PENDING_IDLE_RESET_EVENTS || pendingIdleResetEvents.some((event) => !event || typeof event !== "object" || typeof event.eventId !== "string" || event.eventId.length < 1 || event.eventId.length > 128 || typeof event.occurredAt !== "string" || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(event.occurredAt) || !Number.isFinite(Date.parse(event.occurredAt))) || new Set(pendingIdleResetEvents.map((event) => event.eventId)).size !== pendingIdleResetEvents.length || value.fullBarrier !== null && value.fullBarrier !== "reset_session" && value.fullBarrier !== "nap")
|
|
29684
29838
|
return { kind: "invalid" };
|
|
29685
29839
|
return {
|
|
29686
29840
|
kind: "state",
|
|
@@ -29688,7 +29842,8 @@ function readResumeControlState(timelineDir) {
|
|
|
29688
29842
|
version: 1,
|
|
29689
29843
|
attemptedSessionId: value.attemptedSessionId,
|
|
29690
29844
|
fencedSessionId: value.fencedSessionId,
|
|
29691
|
-
fullBarrier: value.fullBarrier
|
|
29845
|
+
fullBarrier: value.fullBarrier,
|
|
29846
|
+
pendingIdleResetEvents
|
|
29692
29847
|
}
|
|
29693
29848
|
};
|
|
29694
29849
|
} catch {
|
|
@@ -29711,11 +29866,14 @@ function updateResumeControlState(timelineDir, update) {
|
|
|
29711
29866
|
const current = readResumeControlState(timelineDir);
|
|
29712
29867
|
const base = current.kind === "state" ? current.state : EMPTY_RESUME_CONTROL;
|
|
29713
29868
|
const next = update({ ...base });
|
|
29869
|
+
if (!Array.isArray(next.pendingIdleResetEvents) || next.pendingIdleResetEvents.length > MAX_PENDING_IDLE_RESET_EVENTS || next.pendingIdleResetEvents.some((event) => !event || typeof event !== "object" || typeof event.eventId !== "string" || event.eventId.length < 1 || event.eventId.length > 128 || typeof event.occurredAt !== "string" || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(event.occurredAt) || !Number.isFinite(Date.parse(event.occurredAt))) || new Set(next.pendingIdleResetEvents.map((event) => event.eventId)).size !== next.pendingIdleResetEvents.length)
|
|
29870
|
+
return false;
|
|
29714
29871
|
const canonical = {
|
|
29715
29872
|
version: 1,
|
|
29716
29873
|
attemptedSessionId: next.attemptedSessionId,
|
|
29717
29874
|
fencedSessionId: next.fencedSessionId,
|
|
29718
|
-
fullBarrier: next.fullBarrier
|
|
29875
|
+
fullBarrier: next.fullBarrier,
|
|
29876
|
+
pendingIdleResetEvents: next.pendingIdleResetEvents
|
|
29719
29877
|
};
|
|
29720
29878
|
const body = JSON.stringify(canonical) + `
|
|
29721
29879
|
`;
|
|
@@ -30379,7 +30537,7 @@ function createTimelineRecorder(opts) {
|
|
|
30379
30537
|
clearSessionStall(agentId, sessionId) {
|
|
30380
30538
|
return appendStallMarker(agentId, "stall_recovery_clear", sessionId);
|
|
30381
30539
|
},
|
|
30382
|
-
forgetSession(agentId, barrierType = "reset_session", forgottenSessionId) {
|
|
30540
|
+
forgetSession(agentId, barrierType = "reset_session", forgottenSessionId, pendingIdleResetEvent) {
|
|
30383
30541
|
retryPending(agentId);
|
|
30384
30542
|
const dir = dirFor(agentId);
|
|
30385
30543
|
if (!prepareTimelineDirectory(dir))
|
|
@@ -30391,7 +30549,8 @@ function createTimelineRecorder(opts) {
|
|
|
30391
30549
|
...state,
|
|
30392
30550
|
attemptedSessionId: null,
|
|
30393
30551
|
fencedSessionId: null,
|
|
30394
|
-
fullBarrier: barrierType
|
|
30552
|
+
fullBarrier: barrierType,
|
|
30553
|
+
pendingIdleResetEvents: pendingIdleResetEvent && !state.pendingIdleResetEvents.some(({ eventId }) => eventId === pendingIdleResetEvent.eventId) ? [...state.pendingIdleResetEvents, pendingIdleResetEvent] : state.pendingIdleResetEvents
|
|
30395
30554
|
}));
|
|
30396
30555
|
} else if (barrierType === "stall_recovery") {
|
|
30397
30556
|
persisted = updateResumeControlState(dir, (state) => ({
|
|
@@ -30422,6 +30581,19 @@ function createTimelineRecorder(opts) {
|
|
|
30422
30581
|
const result = appendTrackedEntry(dir, createSystemEntry(barrierType, stamp.toISOString(), forgottenSessionId), stamp);
|
|
30423
30582
|
handleTrackedResult(agentId, null, result);
|
|
30424
30583
|
return true;
|
|
30584
|
+
},
|
|
30585
|
+
pendingIdleResetEvents(agentId) {
|
|
30586
|
+
const state = readResumeControlState(dirFor(agentId));
|
|
30587
|
+
return state.kind === "state" ? state.state.pendingIdleResetEvents.map((event) => ({ ...event })) : [];
|
|
30588
|
+
},
|
|
30589
|
+
acknowledgeIdleResetEvent(agentId, eventId) {
|
|
30590
|
+
const dir = dirFor(agentId);
|
|
30591
|
+
if (!prepareTimelineDirectory(dir))
|
|
30592
|
+
return false;
|
|
30593
|
+
return updateResumeControlState(dir, (state) => ({
|
|
30594
|
+
...state,
|
|
30595
|
+
pendingIdleResetEvents: state.pendingIdleResetEvents.filter((event) => event.eventId !== eventId)
|
|
30596
|
+
}));
|
|
30425
30597
|
}
|
|
30426
30598
|
};
|
|
30427
30599
|
function appendStallMarker(agentId, type, sessionId) {
|
|
@@ -30976,6 +31148,8 @@ async function createDaemon(opts) {
|
|
|
30976
31148
|
const emitBotAuditEvent = (agentId, event, context) => {
|
|
30977
31149
|
channelRef?.reportBotAuditEvent?.({
|
|
30978
31150
|
type: "bot_audit_event",
|
|
31151
|
+
...context?.eventId ? { eventId: context.eventId } : {},
|
|
31152
|
+
...context?.occurredAt ? { occurredAt: context.occurredAt } : {},
|
|
30979
31153
|
agentId,
|
|
30980
31154
|
sessionId: context?.sessionId ?? null,
|
|
30981
31155
|
launchId: context?.launchId ?? null,
|
|
@@ -31054,6 +31228,7 @@ async function createDaemon(opts) {
|
|
|
31054
31228
|
typingTracker.clear(agentId);
|
|
31055
31229
|
}
|
|
31056
31230
|
const botsById = new Map;
|
|
31231
|
+
let botCacheReady = false;
|
|
31057
31232
|
async function listMyBotsHttp() {
|
|
31058
31233
|
const res = await fetch(`${opts.serverUrl}/api/community/daemon/bots`, {
|
|
31059
31234
|
method: "GET",
|
|
@@ -31064,22 +31239,27 @@ async function createDaemon(opts) {
|
|
|
31064
31239
|
const json2 = await res.json();
|
|
31065
31240
|
return json2.bots ?? [];
|
|
31066
31241
|
}
|
|
31242
|
+
function replaceBotCache(bots) {
|
|
31243
|
+
botsById.clear();
|
|
31244
|
+
for (const b of bots) {
|
|
31245
|
+
botsById.set(b.id, {
|
|
31246
|
+
name: b.name,
|
|
31247
|
+
discriminator: b.discriminator,
|
|
31248
|
+
description: b.description,
|
|
31249
|
+
ownerName: b.ownerName,
|
|
31250
|
+
ownerDiscriminator: b.ownerDiscriminator
|
|
31251
|
+
});
|
|
31252
|
+
restorePendingIdleResetEvents(b.id);
|
|
31253
|
+
}
|
|
31254
|
+
botCacheReady = true;
|
|
31255
|
+
}
|
|
31067
31256
|
async function coldStartWarmup() {
|
|
31068
31257
|
const start = Date.now();
|
|
31069
31258
|
let attempt = 0;
|
|
31070
31259
|
while (Date.now() - start < WARMUP_CEILING_MS) {
|
|
31071
31260
|
try {
|
|
31072
31261
|
const bots = await listMyBotsHttp();
|
|
31073
|
-
|
|
31074
|
-
for (const b of bots) {
|
|
31075
|
-
botsById.set(b.id, {
|
|
31076
|
-
name: b.name,
|
|
31077
|
-
discriminator: b.discriminator,
|
|
31078
|
-
description: b.description,
|
|
31079
|
-
ownerName: b.ownerName,
|
|
31080
|
-
ownerDiscriminator: b.ownerDiscriminator
|
|
31081
|
-
});
|
|
31082
|
-
}
|
|
31262
|
+
replaceBotCache(bots);
|
|
31083
31263
|
log2.info("cold-start bot-cache warmup succeeded", { bots: bots.length, attempt });
|
|
31084
31264
|
return;
|
|
31085
31265
|
} catch {
|
|
@@ -31164,9 +31344,23 @@ async function createDaemon(opts) {
|
|
|
31164
31344
|
headers: { Authorization: `Bearer ${opts.machineKey}` },
|
|
31165
31345
|
webSocketFactory: opts.webSocketFactory,
|
|
31166
31346
|
onAuthRejected: opts.onAuthRejected,
|
|
31347
|
+
onBotAuditEventAck: ({ agentId, eventId }) => timeline2.acknowledgeIdleResetEvent(agentId, eventId),
|
|
31167
31348
|
logger: log2.child("ws")
|
|
31168
31349
|
});
|
|
31169
31350
|
channelRef = channel2;
|
|
31351
|
+
function restorePendingIdleResetEvents(agentId) {
|
|
31352
|
+
for (const pending of timeline2.pendingIdleResetEvents(agentId)) {
|
|
31353
|
+
channel2.restorePendingBotAuditEvent({
|
|
31354
|
+
type: "bot_audit_event",
|
|
31355
|
+
eventId: pending.eventId,
|
|
31356
|
+
occurredAt: pending.occurredAt,
|
|
31357
|
+
agentId,
|
|
31358
|
+
sessionId: null,
|
|
31359
|
+
launchId: null,
|
|
31360
|
+
event: { kind: "session_reset", payload: { trigger: "idle_timeout" } }
|
|
31361
|
+
});
|
|
31362
|
+
}
|
|
31363
|
+
}
|
|
31170
31364
|
function handleBotFrame(cmd) {
|
|
31171
31365
|
switch (cmd.type) {
|
|
31172
31366
|
case "bot:added":
|
|
@@ -31177,6 +31371,7 @@ async function createDaemon(opts) {
|
|
|
31177
31371
|
ownerName: cmd.ownerName,
|
|
31178
31372
|
ownerDiscriminator: cmd.ownerDiscriminator
|
|
31179
31373
|
});
|
|
31374
|
+
restorePendingIdleResetEvents(cmd.botId);
|
|
31180
31375
|
log2.debug("bot:added", { botId: cmd.botId, name: cmd.name });
|
|
31181
31376
|
break;
|
|
31182
31377
|
case "bot:updated": {
|
|
@@ -31300,7 +31495,14 @@ async function createDaemon(opts) {
|
|
|
31300
31495
|
const statusPath = opts.statusFilePath;
|
|
31301
31496
|
const writeStatus = () => {
|
|
31302
31497
|
const nowMs = Date.now();
|
|
31303
|
-
writeStatusFile(statusPath, {
|
|
31498
|
+
writeStatusFile(statusPath, {
|
|
31499
|
+
writtenAt: nowMs,
|
|
31500
|
+
agentSummary: {
|
|
31501
|
+
total: botCacheReady ? botsById.size : null,
|
|
31502
|
+
running: manager.runningAgentCount()
|
|
31503
|
+
},
|
|
31504
|
+
agents: manager.statusProjection(nowMs)
|
|
31505
|
+
});
|
|
31304
31506
|
};
|
|
31305
31507
|
writeStatus();
|
|
31306
31508
|
statusTimer = setInterval(writeStatus, STATUS_WRITE_INTERVAL_MS);
|
|
@@ -31325,15 +31527,7 @@ async function createDaemon(opts) {
|
|
|
31325
31527
|
if (!botsById.has(agentId)) {
|
|
31326
31528
|
try {
|
|
31327
31529
|
const bots = await listMyBotsHttp();
|
|
31328
|
-
|
|
31329
|
-
botsById.set(b.id, {
|
|
31330
|
-
name: b.name,
|
|
31331
|
-
discriminator: b.discriminator,
|
|
31332
|
-
description: b.description,
|
|
31333
|
-
ownerName: b.ownerName,
|
|
31334
|
-
ownerDiscriminator: b.ownerDiscriminator
|
|
31335
|
-
});
|
|
31336
|
-
}
|
|
31530
|
+
replaceBotCache(bots);
|
|
31337
31531
|
} catch {}
|
|
31338
31532
|
}
|
|
31339
31533
|
if (!botsById.has(agentId)) {
|
|
@@ -33649,8 +33843,13 @@ function daemonList(opts) {
|
|
|
33649
33843
|
if (alive && statusPath) {
|
|
33650
33844
|
const s = daemonStatusFromFile(statusPath, now);
|
|
33651
33845
|
if (s.found) {
|
|
33652
|
-
|
|
33653
|
-
|
|
33846
|
+
if (s.agentSummary) {
|
|
33847
|
+
agents = s.agentSummary.total;
|
|
33848
|
+
running = s.agentSummary.running;
|
|
33849
|
+
} else {
|
|
33850
|
+
agents = s.agents.length;
|
|
33851
|
+
running = s.agents.filter((a) => a.derivedActivity === "running").length;
|
|
33852
|
+
}
|
|
33654
33853
|
lastActiveMs = daemonLastActiveMs(s, now);
|
|
33655
33854
|
}
|
|
33656
33855
|
}
|
|
@@ -33665,7 +33864,24 @@ function daemonList(opts) {
|
|
|
33665
33864
|
return results;
|
|
33666
33865
|
}
|
|
33667
33866
|
var STATUS_STALE_MS = 20000;
|
|
33668
|
-
var MISSING_STATUS = {
|
|
33867
|
+
var MISSING_STATUS = {
|
|
33868
|
+
found: false,
|
|
33869
|
+
ageMs: null,
|
|
33870
|
+
freshness: "missing",
|
|
33871
|
+
writtenAt: null,
|
|
33872
|
+
agentSummary: null,
|
|
33873
|
+
agents: []
|
|
33874
|
+
};
|
|
33875
|
+
function validAgentSummary(value) {
|
|
33876
|
+
if (!value || typeof value !== "object")
|
|
33877
|
+
return null;
|
|
33878
|
+
const summary = value;
|
|
33879
|
+
const validTotal = summary.total === null || Number.isInteger(summary.total) && summary.total >= 0;
|
|
33880
|
+
const validRunning = Number.isInteger(summary.running) && summary.running >= 0;
|
|
33881
|
+
if (!validTotal || !validRunning)
|
|
33882
|
+
return null;
|
|
33883
|
+
return { total: summary.total, running: summary.running };
|
|
33884
|
+
}
|
|
33669
33885
|
function daemonStatusFromFile(statusPath, nowMs) {
|
|
33670
33886
|
if (!fs12.existsSync(statusPath))
|
|
33671
33887
|
return MISSING_STATUS;
|
|
@@ -33677,6 +33893,7 @@ function daemonStatusFromFile(statusPath, nowMs) {
|
|
|
33677
33893
|
ageMs,
|
|
33678
33894
|
freshness: ageMs <= STATUS_STALE_MS ? "fresh" : "stale",
|
|
33679
33895
|
writtenAt: snap.writtenAt,
|
|
33896
|
+
agentSummary: validAgentSummary(snap.agentSummary),
|
|
33680
33897
|
agents: Array.isArray(snap.agents) ? snap.agents : []
|
|
33681
33898
|
};
|
|
33682
33899
|
} catch {
|