@agentchatme/agent-core 0.0.1312 → 0.0.13131
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 +23 -7
- package/dist/{chunk-AGDJ4A6R.js → chunk-M2X5WY7Q.js} +81 -2
- package/dist/chunk-M2X5WY7Q.js.map +1 -0
- package/dist/daemon-entry.d.ts +71 -7
- package/dist/daemon-entry.js +312 -68
- package/dist/daemon-entry.js.map +1 -1
- package/dist/index.d.ts +35 -8
- package/dist/index.js +65 -49
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/dist/chunk-AGDJ4A6R.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
HEARTBEAT_FILE,
|
|
6
6
|
VERSION,
|
|
7
7
|
WireError,
|
|
8
|
+
absoluteUtc,
|
|
8
9
|
acquireLeaderLock,
|
|
9
10
|
alwaysOnHealth,
|
|
10
11
|
alwaysOnOptedOut,
|
|
@@ -14,15 +15,18 @@ import {
|
|
|
14
15
|
atomicWriteFile,
|
|
15
16
|
beat,
|
|
16
17
|
claimReply,
|
|
18
|
+
claimReplyBatch,
|
|
17
19
|
clearAlwaysOnInstalledVersion,
|
|
18
20
|
clearAlwaysOnOptOut,
|
|
19
21
|
clearAlwaysOnWanted,
|
|
20
22
|
clearCredentials,
|
|
23
|
+
clearForegroundTurn,
|
|
21
24
|
clearPending,
|
|
22
25
|
clearSessionActive,
|
|
23
26
|
contextOf,
|
|
24
27
|
credentialsPath,
|
|
25
28
|
external_exports,
|
|
29
|
+
formatWhen,
|
|
26
30
|
getMeLite,
|
|
27
31
|
idle,
|
|
28
32
|
lastDeliveryId,
|
|
@@ -30,19 +34,22 @@ import {
|
|
|
30
34
|
markAlwaysOnInstalledVersion,
|
|
31
35
|
markAlwaysOnOptOut,
|
|
32
36
|
markAlwaysOnWanted,
|
|
37
|
+
markForegroundTurn,
|
|
33
38
|
markSessionActive,
|
|
34
39
|
pendingPath,
|
|
35
40
|
readAlwaysOnInstalledVersion,
|
|
36
41
|
readCredentials,
|
|
37
42
|
readJsonFile,
|
|
38
43
|
readPending,
|
|
44
|
+
relativeAge,
|
|
45
|
+
relativeWhen,
|
|
39
46
|
resolveIdentity,
|
|
40
47
|
statePath,
|
|
41
48
|
syncAck,
|
|
42
49
|
syncPeek,
|
|
43
50
|
writeCredentials,
|
|
44
51
|
writePending
|
|
45
|
-
} from "./chunk-
|
|
52
|
+
} from "./chunk-M2X5WY7Q.js";
|
|
46
53
|
|
|
47
54
|
// src/identity/state.ts
|
|
48
55
|
var SESSION_TTL_MS = 48 * 60 * 60 * 1e3;
|
|
@@ -257,37 +264,6 @@ function stripAllBlocks(existing, start, end) {
|
|
|
257
264
|
return text;
|
|
258
265
|
}
|
|
259
266
|
|
|
260
|
-
// src/util/when.ts
|
|
261
|
-
var SEC = 1e3;
|
|
262
|
-
var MIN = 60 * SEC;
|
|
263
|
-
var HOUR = 60 * MIN;
|
|
264
|
-
var DAY = 24 * HOUR;
|
|
265
|
-
function relativeAge(ms) {
|
|
266
|
-
if (ms < 45 * SEC) return "just now";
|
|
267
|
-
if (ms < 90 * SEC) return "1 minute ago";
|
|
268
|
-
if (ms < 45 * MIN) return `${Math.round(ms / MIN)} minutes ago`;
|
|
269
|
-
if (ms < 90 * MIN) return "1 hour ago";
|
|
270
|
-
if (ms < 22 * HOUR) return `${Math.round(ms / HOUR)} hours ago`;
|
|
271
|
-
if (ms < 36 * HOUR) return "1 day ago";
|
|
272
|
-
return `${Math.round(ms / DAY)} days ago`;
|
|
273
|
-
}
|
|
274
|
-
function absoluteUtc(t) {
|
|
275
|
-
const iso = new Date(t).toISOString();
|
|
276
|
-
return `${iso.slice(0, 10)} ${iso.slice(11, 16)} UTC`;
|
|
277
|
-
}
|
|
278
|
-
function relativeWhen(createdAt, now = Date.now()) {
|
|
279
|
-
if (!createdAt) return "";
|
|
280
|
-
const t = Date.parse(createdAt);
|
|
281
|
-
if (Number.isNaN(t)) return "";
|
|
282
|
-
return relativeAge(Math.max(0, now - t));
|
|
283
|
-
}
|
|
284
|
-
function formatWhen(createdAt, now = Date.now()) {
|
|
285
|
-
if (!createdAt) return "at an unknown time";
|
|
286
|
-
const t = Date.parse(createdAt);
|
|
287
|
-
if (Number.isNaN(t)) return "at an unknown time";
|
|
288
|
-
return `${relativeAge(Math.max(0, now - t))} (${absoluteUtc(t)})`;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
267
|
// src/digest/summary.ts
|
|
292
268
|
var SNIPPET_MAX = 140;
|
|
293
269
|
function snippetOf(row) {
|
|
@@ -309,9 +285,11 @@ function digestConversations(rows, selfHandle = null) {
|
|
|
309
285
|
existing.count += 1;
|
|
310
286
|
if (!existing.senders.includes(sender)) existing.senders.push(sender);
|
|
311
287
|
existing.latestSnippet = snippetOf(row);
|
|
288
|
+
existing.latestMessageId = row.id;
|
|
312
289
|
existing.latestCreatedAt = row.created_at ?? existing.latestCreatedAt;
|
|
313
290
|
existing.groupName = ctx.groupName ?? existing.groupName;
|
|
314
291
|
existing.mentionsYou = existing.mentionsYou || mentionsSelf;
|
|
292
|
+
if (mentionsSelf) existing.mentionedMessageIds.push(row.id);
|
|
315
293
|
} else {
|
|
316
294
|
byConversation.set(row.conversation_id, {
|
|
317
295
|
conversationId: row.conversation_id,
|
|
@@ -319,9 +297,11 @@ function digestConversations(rows, selfHandle = null) {
|
|
|
319
297
|
senders: [sender],
|
|
320
298
|
count: 1,
|
|
321
299
|
latestSnippet: snippetOf(row),
|
|
300
|
+
latestMessageId: row.id,
|
|
322
301
|
latestCreatedAt: row.created_at,
|
|
323
302
|
groupName: ctx.groupName,
|
|
324
|
-
mentionsYou: mentionsSelf
|
|
303
|
+
mentionsYou: mentionsSelf,
|
|
304
|
+
mentionedMessageIds: mentionsSelf ? [row.id] : []
|
|
325
305
|
});
|
|
326
306
|
}
|
|
327
307
|
}
|
|
@@ -335,7 +315,9 @@ function digestLines(digests) {
|
|
|
335
315
|
const age = relativeWhen(d.latestCreatedAt);
|
|
336
316
|
const recency = age ? `, latest ${age}` : "";
|
|
337
317
|
const mention = d.mentionsYou ? " \u2014 mentions you" : "";
|
|
338
|
-
|
|
318
|
+
const attentionIds = d.mentionedMessageIds.slice(-30);
|
|
319
|
+
const attention = attentionIds.length > 0 ? `, attention_message_ids=${JSON.stringify(attentionIds)}${d.mentionedMessageIds.length > attentionIds.length ? ` (+${d.mentionedMessageIds.length - attentionIds.length} older mentions in history)` : ""}` : "";
|
|
320
|
+
return `${i + 1}. ${who} (${count}, ${kind}${recency}${mention}), latest_message_id=${d.latestMessageId}${attention}, preview=${JSON.stringify(d.latestSnippet)}`;
|
|
339
321
|
});
|
|
340
322
|
}
|
|
341
323
|
function formatSessionStart(handle, rows) {
|
|
@@ -350,7 +332,7 @@ function formatSessionStart(handle, rows) {
|
|
|
350
332
|
"",
|
|
351
333
|
...digestLines(digests),
|
|
352
334
|
"",
|
|
353
|
-
"Triage per your AgentChat skill: read a conversation with agentchat_get_conversation before replying; reply only where an open request is addressed to you; finished conversations get silence, not acknowledgments. Mention anything the user should know about."
|
|
335
|
+
"Triage per your AgentChat skill: read a conversation with agentchat_get_conversation before replying, passing its latest_message_id as around_message_id and any listed attention_message_ids exactly; reply only where an open request is addressed to you; finished conversations get silence, not acknowledgments. Mention anything the user should know about."
|
|
354
336
|
].join("\n");
|
|
355
337
|
}
|
|
356
338
|
function formatStopPickup(handle, rows) {
|
|
@@ -364,7 +346,7 @@ function formatStopPickup(handle, rows) {
|
|
|
364
346
|
"",
|
|
365
347
|
...digestLines(digests),
|
|
366
348
|
"",
|
|
367
|
-
"Handle these per your AgentChat skill,
|
|
349
|
+
"Handle these per your AgentChat skill, opening each conversation with agentchat_get_conversation, its latest_message_id as around_message_id, and any listed attention_message_ids exactly. Reply via agentchat_send_message only where warranted \u2014 if nothing is actionable, simply end the turn (silence is a valid outcome)."
|
|
368
350
|
].join("\n");
|
|
369
351
|
}
|
|
370
352
|
function formatAlwaysOnDown(copy) {
|
|
@@ -451,6 +433,7 @@ function renderDeclinedBlock(copy) {
|
|
|
451
433
|
var SESSION_START_PEEK_LIMIT = 100;
|
|
452
434
|
var STOP_PEEK_LIMIT = 50;
|
|
453
435
|
var DEFAULT_MAX_CONTINUATIONS = 5;
|
|
436
|
+
var FOREGROUND_TURN_TTL_SECONDS = 600;
|
|
454
437
|
function hooksDisabled() {
|
|
455
438
|
return process.env["AGENTCHAT_HOOKS_ENABLED"] === "0";
|
|
456
439
|
}
|
|
@@ -479,12 +462,12 @@ function ackableRows(rows) {
|
|
|
479
462
|
return usable;
|
|
480
463
|
}
|
|
481
464
|
async function claimContiguousPrefix(cfg, rows, holder) {
|
|
482
|
-
const
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
465
|
+
const claimed = await claimReplyBatch(
|
|
466
|
+
cfg,
|
|
467
|
+
rows.map((row) => row.id),
|
|
468
|
+
holder
|
|
469
|
+
);
|
|
470
|
+
const prefix = rows.slice(0, claimed);
|
|
488
471
|
if (prefix.length < rows.length) {
|
|
489
472
|
log.info(
|
|
490
473
|
`coexistence: daemon owns ${rows.length - prefix.length} row(s); surfacing ${prefix.length}`
|
|
@@ -507,7 +490,6 @@ async function sessionStart(ctx, input) {
|
|
|
507
490
|
return none;
|
|
508
491
|
}
|
|
509
492
|
const cfg = { apiKey: identity.apiKey, apiBase: identity.apiBase };
|
|
510
|
-
await markSessionActive(cfg);
|
|
511
493
|
const h = alwaysOnHealth(ctx.home);
|
|
512
494
|
const alert = h.wanted && !h.healthy ? formatAlwaysOnDown(ctx.copy) : null;
|
|
513
495
|
const peeked = ackableRows(await syncPeek(cfg, { limit: SESSION_START_PEEK_LIMIT }));
|
|
@@ -531,9 +513,10 @@ async function userPrompt(ctx, input) {
|
|
|
531
513
|
if (hooksDisabled()) return;
|
|
532
514
|
const identity = resolveIdentity(ctx.home);
|
|
533
515
|
if (identity === null) return;
|
|
516
|
+
const cfg = { apiKey: identity.apiKey, apiBase: identity.apiBase };
|
|
517
|
+
await markForegroundTurn(cfg, input.sessionId, FOREGROUND_TURN_TTL_SECONDS);
|
|
534
518
|
const cursor = takePendingAck(ctx.home, input.sessionId);
|
|
535
519
|
if (cursor === null) return;
|
|
536
|
-
const cfg = { apiKey: identity.apiKey, apiBase: identity.apiBase };
|
|
537
520
|
try {
|
|
538
521
|
await syncAck(cfg, cursor);
|
|
539
522
|
} catch (err) {
|
|
@@ -547,43 +530,64 @@ async function userPrompt(ctx, input) {
|
|
|
547
530
|
async function stop(ctx, input) {
|
|
548
531
|
const none = { reason: null, commit: async () => {
|
|
549
532
|
} };
|
|
533
|
+
let cfg = null;
|
|
550
534
|
try {
|
|
551
535
|
if (hooksDisabled()) return none;
|
|
552
536
|
const identity = resolveIdentity(ctx.home);
|
|
553
537
|
if (identity === null) return none;
|
|
554
|
-
|
|
555
|
-
await
|
|
538
|
+
cfg = { apiKey: identity.apiKey, apiBase: identity.apiBase };
|
|
539
|
+
await markForegroundTurn(cfg, input.sessionId, FOREGROUND_TURN_TTL_SECONDS);
|
|
556
540
|
const cap = maxContinuations();
|
|
557
541
|
if (getContinuations(ctx.home, input.sessionId) >= cap) {
|
|
558
542
|
log.info(
|
|
559
543
|
`stop hook: continuation cap (${cap}) reached for ${input.sessionId}; leaving inbox queued`
|
|
560
544
|
);
|
|
545
|
+
await clearForegroundTurn(cfg, input.sessionId);
|
|
561
546
|
return none;
|
|
562
547
|
}
|
|
563
548
|
const peeked = ackableRows(await syncPeek(cfg, { limit: STOP_PEEK_LIMIT }));
|
|
564
|
-
if (peeked.length === 0)
|
|
549
|
+
if (peeked.length === 0) {
|
|
550
|
+
await clearForegroundTurn(cfg, input.sessionId);
|
|
551
|
+
return none;
|
|
552
|
+
}
|
|
565
553
|
const rows = await claimContiguousPrefix(cfg, peeked, `session:${input.sessionId}`);
|
|
566
|
-
if (rows.length === 0)
|
|
554
|
+
if (rows.length === 0) {
|
|
555
|
+
await clearForegroundTurn(cfg, input.sessionId);
|
|
556
|
+
return none;
|
|
557
|
+
}
|
|
567
558
|
recordContinuation(ctx.home, input.sessionId);
|
|
568
559
|
const handle = await resolveHandle(cfg, identity.handle);
|
|
569
560
|
const reason = formatStopPickup(handle, rows);
|
|
570
561
|
const cursor = lastDeliveryId(rows);
|
|
562
|
+
const claimedCfg = cfg;
|
|
571
563
|
return {
|
|
572
564
|
reason,
|
|
573
565
|
commit: async () => {
|
|
574
566
|
if (cursor === null) return;
|
|
575
567
|
try {
|
|
576
|
-
await syncAck(
|
|
568
|
+
await syncAck(claimedCfg, cursor);
|
|
577
569
|
} catch (err) {
|
|
578
570
|
log.warn(`stop ack failed (messages stay queued): ${String(err)}`);
|
|
579
571
|
}
|
|
580
572
|
}
|
|
581
573
|
};
|
|
582
574
|
} catch (err) {
|
|
575
|
+
if (cfg !== null) await clearForegroundTurn(cfg, input.sessionId);
|
|
583
576
|
log.warn(`stop hook degraded to no-op: ${String(err)}`);
|
|
584
577
|
return none;
|
|
585
578
|
}
|
|
586
579
|
}
|
|
580
|
+
async function sessionEnd(ctx, input) {
|
|
581
|
+
try {
|
|
582
|
+
if (hooksDisabled()) return;
|
|
583
|
+
const identity = resolveIdentity(ctx.home);
|
|
584
|
+
if (identity === null) return;
|
|
585
|
+
const cfg = { apiKey: identity.apiKey, apiBase: identity.apiBase };
|
|
586
|
+
await clearForegroundTurn(cfg, input.sessionId);
|
|
587
|
+
} catch (err) {
|
|
588
|
+
log.warn(`session-end hook degraded to no-op: ${String(err)}`);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
587
591
|
|
|
588
592
|
// src/hooks/hook-input.ts
|
|
589
593
|
async function readHookInput(stream = process.stdin) {
|
|
@@ -653,6 +657,14 @@ function createHookRunners(context, dialect) {
|
|
|
653
657
|
} catch (err) {
|
|
654
658
|
log.warn(`stop hook degraded to no-op: ${String(err)}`);
|
|
655
659
|
}
|
|
660
|
+
},
|
|
661
|
+
async runSessionEnd() {
|
|
662
|
+
try {
|
|
663
|
+
const input = await readHookInput();
|
|
664
|
+
await sessionEnd(context(), input);
|
|
665
|
+
} catch (err) {
|
|
666
|
+
log.warn(`session-end hook degraded to no-op: ${String(err)}`);
|
|
667
|
+
}
|
|
656
668
|
}
|
|
657
669
|
};
|
|
658
670
|
}
|
|
@@ -1696,10 +1708,12 @@ export {
|
|
|
1696
1708
|
atomicWriteFile,
|
|
1697
1709
|
beat,
|
|
1698
1710
|
claimReply,
|
|
1711
|
+
claimReplyBatch,
|
|
1699
1712
|
clearAlwaysOnInstalledVersion,
|
|
1700
1713
|
clearAlwaysOnOptOut,
|
|
1701
1714
|
clearAlwaysOnWanted,
|
|
1702
1715
|
clearCredentials,
|
|
1716
|
+
clearForegroundTurn,
|
|
1703
1717
|
clearOfferDeclined,
|
|
1704
1718
|
clearPending,
|
|
1705
1719
|
clearSessionActive,
|
|
@@ -1724,6 +1738,7 @@ export {
|
|
|
1724
1738
|
markAlwaysOnInstalledVersion,
|
|
1725
1739
|
markAlwaysOnOptOut,
|
|
1726
1740
|
markAlwaysOnWanted,
|
|
1741
|
+
markForegroundTurn,
|
|
1727
1742
|
markSessionActive,
|
|
1728
1743
|
offerDeclined,
|
|
1729
1744
|
pendingPath,
|
|
@@ -1751,6 +1766,7 @@ export {
|
|
|
1751
1766
|
serviceDefinitionCurrent,
|
|
1752
1767
|
serviceInstalled,
|
|
1753
1768
|
serviceStatus,
|
|
1769
|
+
sessionEnd,
|
|
1754
1770
|
sessionStart,
|
|
1755
1771
|
setPendingAck,
|
|
1756
1772
|
shouldOfferRegistration,
|