@agentchatme/agent-core 0.0.1313 → 0.0.1313111
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/README.md +27 -8
- package/dist/{chunk-27XDHOL3.js → chunk-YWX7E5VW.js} +20 -2
- package/dist/chunk-YWX7E5VW.js.map +1 -0
- package/dist/daemon-entry.d.ts +14 -5
- package/dist/daemon-entry.js +103 -29
- package/dist/daemon-entry.js.map +1 -1
- package/dist/index.d.ts +52 -22
- package/dist/index.js +141 -54
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/dist/chunk-27XDHOL3.js.map +0 -1
package/dist/daemon-entry.d.ts
CHANGED
|
@@ -102,6 +102,14 @@ interface CoordConfig {
|
|
|
102
102
|
holder: string;
|
|
103
103
|
timeoutMs?: number;
|
|
104
104
|
}
|
|
105
|
+
interface ClaimOutcome {
|
|
106
|
+
claimed: boolean;
|
|
107
|
+
deferred: boolean;
|
|
108
|
+
}
|
|
109
|
+
interface ClaimBatchOutcome {
|
|
110
|
+
claimedCount: number;
|
|
111
|
+
deferred: boolean;
|
|
112
|
+
}
|
|
105
113
|
declare class ReplyCoord {
|
|
106
114
|
private readonly cfg;
|
|
107
115
|
constructor(cfg: CoordConfig);
|
|
@@ -109,17 +117,16 @@ declare class ReplyCoord {
|
|
|
109
117
|
/** Is the agent's live coding session actively working? Fail-open → FALSE. */
|
|
110
118
|
isSessionActive(): Promise<boolean>;
|
|
111
119
|
/**
|
|
112
|
-
* Claim the sole right to reply to a message
|
|
113
|
-
*
|
|
114
|
-
* → TRUE (reply anyway rather than drop).
|
|
120
|
+
* Claim the sole right to reply to a message, atomically respecting any
|
|
121
|
+
* foreground turn. Fail-open → claimed (reply anyway rather than drop).
|
|
115
122
|
*/
|
|
116
|
-
claim(messageId: string): Promise<
|
|
123
|
+
claim(messageId: string): Promise<ClaimOutcome>;
|
|
117
124
|
/**
|
|
118
125
|
* Claim the contiguous oldest-first prefix of one conversation batch.
|
|
119
126
|
* Falls back to ordered single-message claims against an older API server;
|
|
120
127
|
* all other coordination failures remain fail-open.
|
|
121
128
|
*/
|
|
122
|
-
claimBatch(messageIds: string[]): Promise<
|
|
129
|
+
claimBatch(messageIds: string[]): Promise<ClaimBatchOutcome>;
|
|
123
130
|
}
|
|
124
131
|
|
|
125
132
|
interface TurnMentionContext {
|
|
@@ -281,6 +288,7 @@ declare class Daemon {
|
|
|
281
288
|
private pending;
|
|
282
289
|
private inFlight;
|
|
283
290
|
private readonly waiters;
|
|
291
|
+
private foregroundClaimsBlockedUntil;
|
|
284
292
|
private stopping;
|
|
285
293
|
private heartbeatTimer;
|
|
286
294
|
constructor(cfg: DaemonConfig, adapter: RuntimeAdapter, ws?: AgentWsClient, // injectable for tests; defaults to a real socket
|
|
@@ -295,6 +303,7 @@ declare class Daemon {
|
|
|
295
303
|
/** Process bounded backlog snapshots, in arrival order within a conversation. */
|
|
296
304
|
private drainConversation;
|
|
297
305
|
private handleNextBatch;
|
|
306
|
+
private waitForForegroundClaimWindow;
|
|
298
307
|
private turnContext;
|
|
299
308
|
private markHandled;
|
|
300
309
|
private markNoLongerPending;
|
package/dist/daemon-entry.js
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
idle,
|
|
11
11
|
log,
|
|
12
12
|
resolveIdentity
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-YWX7E5VW.js";
|
|
14
14
|
|
|
15
15
|
// src/daemon/ws-client.ts
|
|
16
16
|
import { WebSocket } from "ws";
|
|
@@ -309,20 +309,23 @@ var ReplyCoord = class {
|
|
|
309
309
|
}
|
|
310
310
|
}
|
|
311
311
|
/**
|
|
312
|
-
* Claim the sole right to reply to a message
|
|
313
|
-
*
|
|
314
|
-
* → TRUE (reply anyway rather than drop).
|
|
312
|
+
* Claim the sole right to reply to a message, atomically respecting any
|
|
313
|
+
* foreground turn. Fail-open → claimed (reply anyway rather than drop).
|
|
315
314
|
*/
|
|
316
315
|
async claim(messageId) {
|
|
317
316
|
try {
|
|
318
317
|
const d = await this.req("POST", "/v1/reply/claim", {
|
|
319
318
|
message_id: messageId,
|
|
320
|
-
holder: this.cfg.holder
|
|
319
|
+
holder: this.cfg.holder,
|
|
320
|
+
defer_if_active: true
|
|
321
321
|
});
|
|
322
|
-
return
|
|
322
|
+
return {
|
|
323
|
+
claimed: d?.claimed !== false,
|
|
324
|
+
deferred: d?.deferred === true
|
|
325
|
+
};
|
|
323
326
|
} catch (err) {
|
|
324
327
|
log.debug(`coord claim failed (proceeding): ${String(err)}`);
|
|
325
|
-
return true;
|
|
328
|
+
return { claimed: true, deferred: false };
|
|
326
329
|
}
|
|
327
330
|
}
|
|
328
331
|
/**
|
|
@@ -331,26 +334,33 @@ var ReplyCoord = class {
|
|
|
331
334
|
* all other coordination failures remain fail-open.
|
|
332
335
|
*/
|
|
333
336
|
async claimBatch(messageIds) {
|
|
334
|
-
if (messageIds.length === 0) return 0;
|
|
337
|
+
if (messageIds.length === 0) return { claimedCount: 0, deferred: false };
|
|
335
338
|
try {
|
|
336
339
|
const d = await this.req("POST", "/v1/reply/claim-batch", {
|
|
337
340
|
message_ids: messageIds,
|
|
338
|
-
holder: this.cfg.holder
|
|
341
|
+
holder: this.cfg.holder,
|
|
342
|
+
defer_if_active: true
|
|
339
343
|
});
|
|
340
344
|
const count = d?.claimed_count;
|
|
341
|
-
return
|
|
345
|
+
return {
|
|
346
|
+
claimedCount: Number.isInteger(count) && count >= 0 && count <= messageIds.length ? count : messageIds.length,
|
|
347
|
+
deferred: d?.deferred === true
|
|
348
|
+
};
|
|
342
349
|
} catch (err) {
|
|
343
350
|
if (!/reply-coord (404|405)\b/.test(String(err))) {
|
|
344
351
|
log.debug(`coord batch claim failed (proceeding with all): ${String(err)}`);
|
|
345
|
-
return messageIds.length;
|
|
352
|
+
return { claimedCount: messageIds.length, deferred: false };
|
|
346
353
|
}
|
|
347
354
|
}
|
|
348
355
|
let claimed = 0;
|
|
349
356
|
for (const messageId of messageIds) {
|
|
350
|
-
|
|
357
|
+
const outcome = await this.claim(messageId);
|
|
358
|
+
if (!outcome.claimed) {
|
|
359
|
+
return { claimedCount: claimed, deferred: outcome.deferred };
|
|
360
|
+
}
|
|
351
361
|
claimed += 1;
|
|
352
362
|
}
|
|
353
|
-
return claimed;
|
|
363
|
+
return { claimedCount: claimed, deferred: false };
|
|
354
364
|
}
|
|
355
365
|
};
|
|
356
366
|
|
|
@@ -515,7 +525,10 @@ var RETRY_MAX_MS = Math.max(
|
|
|
515
525
|
RETRY_BASE_MS,
|
|
516
526
|
positiveBoundedEnv("AGENTCHATD_RETRY_MAX_MS", 5 * 6e4)
|
|
517
527
|
);
|
|
518
|
-
var
|
|
528
|
+
var FOREGROUND_RECHECK_MS = positiveBoundedEnv(
|
|
529
|
+
"AGENTCHATD_FOREGROUND_RECHECK_MS",
|
|
530
|
+
2e3
|
|
531
|
+
);
|
|
519
532
|
var delay = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
520
533
|
function retryDelay(attempt) {
|
|
521
534
|
return Math.min(RETRY_BASE_MS * 2 ** Math.min(20, Math.max(0, attempt - 1)), RETRY_MAX_MS);
|
|
@@ -577,6 +590,10 @@ var Daemon = class {
|
|
|
577
590
|
pending = 0;
|
|
578
591
|
inFlight = 0;
|
|
579
592
|
waiters = [];
|
|
593
|
+
// Identity-wide foreground priority, learned from any deferred claim. Every
|
|
594
|
+
// conversation shares this window so a large multi-conversation backlog
|
|
595
|
+
// cannot turn into one polling loop per conversation.
|
|
596
|
+
foregroundClaimsBlockedUntil = 0;
|
|
580
597
|
stopping = false;
|
|
581
598
|
heartbeatTimer = null;
|
|
582
599
|
async start() {
|
|
@@ -648,14 +665,10 @@ var Daemon = class {
|
|
|
648
665
|
this.convQueues.get(conversationId)?.shift();
|
|
649
666
|
return;
|
|
650
667
|
}
|
|
651
|
-
if (await this.coord.isSessionActive()) {
|
|
652
|
-
log.info(`msg ${first.id}: live session active \u2014 yielding for ${YIELD_MS}ms`);
|
|
653
|
-
await delay(YIELD_MS);
|
|
654
|
-
if (this.stopping) return;
|
|
655
|
-
}
|
|
656
668
|
await this.acquireSlot();
|
|
657
669
|
let slotHeld = true;
|
|
658
670
|
try {
|
|
671
|
+
await this.waitForForegroundClaimWindow();
|
|
659
672
|
if (this.stopping) {
|
|
660
673
|
return;
|
|
661
674
|
}
|
|
@@ -664,23 +677,77 @@ var Daemon = class {
|
|
|
664
677
|
const queue = this.convQueues.get(conversationId);
|
|
665
678
|
if (!queue || queue.length === 0) return;
|
|
666
679
|
const candidates = queue.splice(0, MAX_BATCH_MESSAGES);
|
|
667
|
-
const
|
|
680
|
+
const claim = await this.coord.claimBatch(
|
|
668
681
|
candidates.map((row) => row.id)
|
|
669
682
|
);
|
|
670
|
-
const
|
|
683
|
+
const claimedCount = claim.claimedCount;
|
|
684
|
+
let batch = candidates.slice(0, claimedCount);
|
|
671
685
|
if (claimedCount < candidates.length) {
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
686
|
+
if (claim.deferred) {
|
|
687
|
+
this.foregroundClaimsBlockedUntil = Math.max(
|
|
688
|
+
this.foregroundClaimsBlockedUntil,
|
|
689
|
+
Date.now() + FOREGROUND_RECHECK_MS
|
|
690
|
+
);
|
|
691
|
+
const deferred = candidates.slice(claimedCount);
|
|
692
|
+
if (deferred.length > 0) {
|
|
693
|
+
const current = this.convQueues.get(conversationId) ?? [];
|
|
694
|
+
this.convQueues.set(conversationId, [...deferred, ...current]);
|
|
695
|
+
}
|
|
696
|
+
log.info(
|
|
697
|
+
`msg ${deferred[0]?.id}: foreground turn owns priority \u2014 deferring daemon claim`
|
|
698
|
+
);
|
|
699
|
+
} else {
|
|
700
|
+
const conflict = candidates[claimedCount];
|
|
701
|
+
log.info(`msg ${conflict.id}: claimed by the live session \u2014 standing down`);
|
|
702
|
+
this.seen.delete(conflict.id);
|
|
703
|
+
this.markNoLongerPending();
|
|
704
|
+
const unclaimedTail = candidates.slice(claimedCount + 1);
|
|
705
|
+
if (unclaimedTail.length > 0) {
|
|
706
|
+
const current = this.convQueues.get(conversationId) ?? [];
|
|
707
|
+
this.convQueues.set(conversationId, [...unclaimedTail, ...current]);
|
|
708
|
+
}
|
|
680
709
|
}
|
|
681
710
|
}
|
|
682
711
|
if (batch.length === 0) return;
|
|
683
712
|
while (!this.stopping) {
|
|
713
|
+
const renewed = await this.coord.claimBatch(batch.map((row) => row.id));
|
|
714
|
+
if (renewed.claimedCount < batch.length) {
|
|
715
|
+
const lost = batch.slice(renewed.claimedCount);
|
|
716
|
+
batch = batch.slice(0, renewed.claimedCount);
|
|
717
|
+
if (renewed.deferred) {
|
|
718
|
+
this.foregroundClaimsBlockedUntil = Math.max(
|
|
719
|
+
this.foregroundClaimsBlockedUntil,
|
|
720
|
+
Date.now() + FOREGROUND_RECHECK_MS
|
|
721
|
+
);
|
|
722
|
+
for (const row of lost) {
|
|
723
|
+
const state = this.seen.get(row.id);
|
|
724
|
+
if (state) {
|
|
725
|
+
state.status = "queued";
|
|
726
|
+
state.updatedAt = Date.now();
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
const current = this.convQueues.get(conversationId) ?? [];
|
|
730
|
+
this.convQueues.set(conversationId, [...lost, ...current]);
|
|
731
|
+
} else {
|
|
732
|
+
const conflict = lost[0];
|
|
733
|
+
log.info(`msg ${conflict.id}: renewal lost to a live session \u2014 standing down`);
|
|
734
|
+
this.seen.delete(conflict.id);
|
|
735
|
+
this.markNoLongerPending();
|
|
736
|
+
const tail = lost.slice(1);
|
|
737
|
+
for (const row of tail) {
|
|
738
|
+
const state = this.seen.get(row.id);
|
|
739
|
+
if (state) {
|
|
740
|
+
state.status = "queued";
|
|
741
|
+
state.updatedAt = Date.now();
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
if (tail.length > 0) {
|
|
745
|
+
const current = this.convQueues.get(conversationId) ?? [];
|
|
746
|
+
this.convQueues.set(conversationId, [...tail, ...current]);
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
if (batch.length === 0) return;
|
|
684
751
|
const states = batch.map((row) => this.seen.get(row.id));
|
|
685
752
|
if (states.some(
|
|
686
753
|
(state) => state === void 0 || state.status === "handled"
|
|
@@ -736,6 +803,13 @@ var Daemon = class {
|
|
|
736
803
|
if (slotHeld) this.releaseSlot();
|
|
737
804
|
}
|
|
738
805
|
}
|
|
806
|
+
async waitForForegroundClaimWindow() {
|
|
807
|
+
while (!this.stopping) {
|
|
808
|
+
const remaining = this.foregroundClaimsBlockedUntil - Date.now();
|
|
809
|
+
if (remaining <= 0) return;
|
|
810
|
+
await delay(remaining);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
739
813
|
turnContext(batch) {
|
|
740
814
|
const focus = batch[batch.length - 1];
|
|
741
815
|
const oldest = batch[0];
|