@agentchatme/agent-core 0.0.1311 → 0.0.1313

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 CHANGED
@@ -32,7 +32,14 @@ Neither bug came from sharing protocol code. Both came from a single command sur
32
32
  | Identity flows — register / login / recover / status / logout / doctor | How to render its anchor |
33
33
  | Session digest text | What JSON shape its hooks emit (`dialect`) |
34
34
  | Hook state machine (continuation cap, ack cursor) | How to spawn a headless turn of its runtime (`RuntimeAdapter`) |
35
- | Daemon — the loop, WS client, leader lock, service install | Its packaging and front door |
35
+ | Daemon — loop, WS client, canonical delivery prompt, service install | Its packaging and front door |
36
+
37
+ An unattended turn represents one bounded, same-conversation backlog (up to 30
38
+ durable deliveries). The newest message is the explicit focus, earlier pending
39
+ messages remain chronological context, and exact group-mention ids are surfaced
40
+ as attention. Both host adapters open the same compact, message-anchored
41
+ conversation context through the MCP server. Host adapters do not maintain
42
+ separate prompt dialects.
36
43
 
37
44
  The test for which column something belongs in: **if changing it requires a
38
45
  matching change on the server, it lives here; if changing it requires reading
@@ -93,6 +100,14 @@ import { runDaemon } from '@agentchatme/agent-core/daemon'
93
100
  await runDaemon({ home: profile.home(), adapter: new MyRuntimeAdapter(...) })
94
101
  ```
95
102
 
103
+ The daemon coalesces a burst or reconnect backlog from one conversation into one
104
+ bounded runtime turn, focused on its newest message. A later arrival cannot join
105
+ a batch once its turn has started. Turns remain ordered within a conversation
106
+ and may run concurrently across different conversations. Every delivery in the
107
+ batch is acknowledged only after the shared turn succeeds; a failure retries
108
+ the same frozen batch with capped exponential backoff instead of dropping or
109
+ partially acknowledging it.
110
+
96
111
  ## Development
97
112
 
98
113
  ```
@@ -25,7 +25,7 @@ var log = {
25
25
  };
26
26
 
27
27
  // src/version.ts
28
- var VERSION = "0.0.1311";
28
+ var VERSION = "0.0.1313";
29
29
 
30
30
  // src/client-identity.ts
31
31
  var CODING_AGENTS_CLIENT_IDENTITY = {
@@ -37,6 +37,37 @@ var CODING_AGENTS_CLIENT_HEADERS = {
37
37
  "X-AgentChat-Client-Version": CODING_AGENTS_CLIENT_IDENTITY.version
38
38
  };
39
39
 
40
+ // src/util/when.ts
41
+ var SEC = 1e3;
42
+ var MIN = 60 * SEC;
43
+ var HOUR = 60 * MIN;
44
+ var DAY = 24 * HOUR;
45
+ function relativeAge(ms) {
46
+ if (ms < 45 * SEC) return "just now";
47
+ if (ms < 90 * SEC) return "1 minute ago";
48
+ if (ms < 45 * MIN) return `${Math.round(ms / MIN)} minutes ago`;
49
+ if (ms < 90 * MIN) return "1 hour ago";
50
+ if (ms < 22 * HOUR) return `${Math.round(ms / HOUR)} hours ago`;
51
+ if (ms < 36 * HOUR) return "1 day ago";
52
+ return `${Math.round(ms / DAY)} days ago`;
53
+ }
54
+ function absoluteUtc(t) {
55
+ const iso = new Date(t).toISOString();
56
+ return `${iso.slice(0, 10)} ${iso.slice(11, 16)} UTC`;
57
+ }
58
+ function relativeWhen(createdAt, now = Date.now()) {
59
+ if (!createdAt) return "";
60
+ const t = Date.parse(createdAt);
61
+ if (Number.isNaN(t)) return "";
62
+ return relativeAge(Math.max(0, now - t));
63
+ }
64
+ function formatWhen(createdAt, now = Date.now()) {
65
+ if (!createdAt) return "at an unknown time";
66
+ const t = Date.parse(createdAt);
67
+ if (Number.isNaN(t)) return "at an unknown time";
68
+ return `${relativeAge(Math.max(0, now - t))} (${absoluteUtc(t)})`;
69
+ }
70
+
40
71
  // src/daemon/leader-lock.ts
41
72
  import * as fs from "fs";
42
73
  import * as path from "path";
@@ -108,6 +139,23 @@ function atomicWriteFile(filePath, data, mode) {
108
139
  fs2.chmodSync(filePath, mode);
109
140
  }
110
141
  }
142
+ function atomicCopyFile(source, destination, mode = 493) {
143
+ const dir = path2.dirname(destination);
144
+ fs2.mkdirSync(dir, { recursive: true, mode: 448 });
145
+ const tmp = path2.join(dir, `.${path2.basename(destination)}.${process.pid}.tmp`);
146
+ try {
147
+ fs2.copyFileSync(source, tmp);
148
+ fs2.chmodSync(tmp, mode);
149
+ fs2.renameSync(tmp, destination);
150
+ fs2.chmodSync(destination, mode);
151
+ } catch (err) {
152
+ try {
153
+ fs2.rmSync(tmp, { force: true });
154
+ } catch {
155
+ }
156
+ throw err;
157
+ }
158
+ }
111
159
  function readJsonFile(filePath) {
112
160
  try {
113
161
  const raw = fs2.readFileSync(filePath, "utf-8");
@@ -4265,8 +4313,11 @@ var SyncRowSchema = external_exports.object({
4265
4313
  // fallback against a future server-side rename).
4266
4314
  sender: external_exports.string().optional(),
4267
4315
  sender_handle: external_exports.string().optional(),
4316
+ seq: external_exports.number().optional(),
4268
4317
  type: external_exports.string().optional(),
4269
4318
  content: external_exports.record(external_exports.unknown()).optional(),
4319
+ metadata: external_exports.record(external_exports.unknown()).optional(),
4320
+ status: external_exports.string().optional(),
4270
4321
  created_at: external_exports.string().optional()
4271
4322
  }).passthrough();
4272
4323
  function contextOf(row) {
@@ -4371,6 +4422,28 @@ async function claimReply(cfg, messageId, holder) {
4371
4422
  return true;
4372
4423
  }
4373
4424
  }
4425
+ async function claimReplyBatch(cfg, messageIds, holder) {
4426
+ if (messageIds.length === 0) return 0;
4427
+ try {
4428
+ const data = await request(cfg, "POST", "/v1/reply/claim-batch", {
4429
+ message_ids: messageIds,
4430
+ holder
4431
+ });
4432
+ const parsed = external_exports.object({ claimed_count: external_exports.number().int().min(0).max(messageIds.length) }).passthrough().safeParse(data);
4433
+ return parsed.success ? parsed.data.claimed_count : messageIds.length;
4434
+ } catch (err) {
4435
+ if (!/AgentChat API (404|405)\b/.test(String(err))) {
4436
+ log.warn(`reply-batch-claim failed (surfacing all): ${String(err)}`);
4437
+ return messageIds.length;
4438
+ }
4439
+ }
4440
+ let claimed = 0;
4441
+ for (const messageId of messageIds) {
4442
+ if (!await claimReply(cfg, messageId, holder)) break;
4443
+ claimed += 1;
4444
+ }
4445
+ return claimed;
4446
+ }
4374
4447
  function lastDeliveryId(rows) {
4375
4448
  for (let i = rows.length - 1; i >= 0; i--) {
4376
4449
  const id = rows[i]?.delivery_id;
@@ -4383,6 +4456,7 @@ function lastDeliveryId(rows) {
4383
4456
  import * as fs4 from "fs";
4384
4457
  import * as path4 from "path";
4385
4458
  var ALWAYS_ON_WANTED = "always-on.wanted";
4459
+ var ALWAYS_ON_INSTALLED_VERSION = "always-on.installed-version";
4386
4460
  var HEARTBEAT_FILE = "daemon.heartbeat";
4387
4461
  var HEARTBEAT_STALE_MS = 3 * 6e4;
4388
4462
  function markAlwaysOnWanted(home) {
@@ -4401,6 +4475,24 @@ function clearAlwaysOnWanted(home) {
4401
4475
  function alwaysOnWanted(home) {
4402
4476
  return fs4.existsSync(path4.join(home, ALWAYS_ON_WANTED));
4403
4477
  }
4478
+ function readAlwaysOnInstalledVersion(home) {
4479
+ try {
4480
+ const version = fs4.readFileSync(path4.join(home, ALWAYS_ON_INSTALLED_VERSION), "utf-8").trim();
4481
+ return version.length > 0 ? version : null;
4482
+ } catch {
4483
+ return null;
4484
+ }
4485
+ }
4486
+ function markAlwaysOnInstalledVersion(home, version) {
4487
+ atomicWriteFile(path4.join(home, ALWAYS_ON_INSTALLED_VERSION), `${version}
4488
+ `, 384);
4489
+ }
4490
+ function clearAlwaysOnInstalledVersion(home) {
4491
+ try {
4492
+ fs4.rmSync(path4.join(home, ALWAYS_ON_INSTALLED_VERSION), { force: true });
4493
+ } catch {
4494
+ }
4495
+ }
4404
4496
  var ALWAYS_ON_OPTOUT = "always-on.optout";
4405
4497
  function markAlwaysOnOptOut(home) {
4406
4498
  try {
@@ -4461,8 +4553,13 @@ export {
4461
4553
  VERSION,
4462
4554
  CODING_AGENTS_CLIENT_IDENTITY,
4463
4555
  CODING_AGENTS_CLIENT_HEADERS,
4556
+ relativeAge,
4557
+ absoluteUtc,
4558
+ relativeWhen,
4559
+ formatWhen,
4464
4560
  acquireLeaderLock,
4465
4561
  atomicWriteFile,
4562
+ atomicCopyFile,
4466
4563
  readJsonFile,
4467
4564
  DEFAULT_API_BASE,
4468
4565
  credentialsPath,
@@ -4483,11 +4580,15 @@ export {
4483
4580
  markSessionActive,
4484
4581
  clearSessionActive,
4485
4582
  claimReply,
4583
+ claimReplyBatch,
4486
4584
  lastDeliveryId,
4487
4585
  HEARTBEAT_FILE,
4488
4586
  markAlwaysOnWanted,
4489
4587
  clearAlwaysOnWanted,
4490
4588
  alwaysOnWanted,
4589
+ readAlwaysOnInstalledVersion,
4590
+ markAlwaysOnInstalledVersion,
4591
+ clearAlwaysOnInstalledVersion,
4491
4592
  markAlwaysOnOptOut,
4492
4593
  clearAlwaysOnOptOut,
4493
4594
  alwaysOnOptedOut,
@@ -4496,4 +4597,4 @@ export {
4496
4597
  alwaysOnState,
4497
4598
  alwaysOnHealth
4498
4599
  };
4499
- //# sourceMappingURL=chunk-ER4AFPH7.js.map
4600
+ //# sourceMappingURL=chunk-27XDHOL3.js.map