@agentconnect.md/daemon 1.0.0-rc.17 → 1.0.0-rc.19

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/index.js CHANGED
@@ -12,6 +12,7 @@ import { createInterface } from "node:readline";
12
12
  import { freemem, homedir, hostname, loadavg, totalmem, type } from "node:os";
13
13
  import { spawn as spawn$1 } from "child_process";
14
14
  import { Readable, Writable } from "node:stream";
15
+ import { MAX_FRAME_BYTES, buildEnvelope, decodeEnvelope, encode, isFrame } from "@agentconnect.md/protocol";
15
16
  import { randomUUID } from "node:crypto";
16
17
  import { lstat, open, readdir, realpath, stat as stat$1 } from "node:fs/promises";
17
18
  import { DatabaseSync } from "node:sqlite";
@@ -16871,535 +16872,6 @@ async function runChat(opts) {
16871
16872
  //#region src/version.ts
16872
16873
  const DAEMON_VERSION = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version;
16873
16874
  //#endregion
16874
- //#region ../protocol/src/envelope.ts
16875
- /**
16876
- * The protocol envelope — every frame, both directions, is wrapped in this.
16877
- * Mirrors daemon-cp-ws-protocol.md §1.1.
16878
- *
16879
- * `payload` is left as `unknown` here and validated by the per-type schema in
16880
- * `frames/*` via `FRAME_SCHEMAS[type]` (see `frame.ts`). This two-step parse is
16881
- * what lets the codec answer an unknown `type` with `UNKNOWN_FRAME` (a REP)
16882
- * instead of a hard close — forward-compat, protocol §1.
16883
- */
16884
- const Envelope = object({
16885
- v: literal(1),
16886
- id: string().uuid(),
16887
- ts: string().datetime(),
16888
- type: string(),
16889
- corr: string().uuid().optional(),
16890
- payload: unknown()
16891
- });
16892
- object({
16893
- epoch: number().int(),
16894
- seq: number().int().optional(),
16895
- agentId: string().uuid().optional(),
16896
- launchId: string().uuid().optional()
16897
- });
16898
- /** The all-zero UUID used when a frame is malformed past the point of reading `id`. */
16899
- const NIL_UUID = "00000000-0000-0000-0000-000000000000";
16900
- //#endregion
16901
- //#region ../protocol/src/frames/auth.ts
16902
- /**
16903
- * Auth & identity — protocol §3.1 / §3.2.
16904
- *
16905
- * `auth` is the first frame after the socket opens. `auth/ok` carries the
16906
- * minted `sessionEpoch` (the global fencing token), heartbeat cadence, and the
16907
- * resume verdict.
16908
- */
16909
- const AuthReq = object({
16910
- daemonToken: string(),
16911
- daemonId: string().uuid().optional(),
16912
- machineId: string().uuid().optional(),
16913
- attestation: string().optional(),
16914
- agentVersion: string(),
16915
- resume: object({
16916
- lastEpoch: number().int(),
16917
- lastRecvSeq: record(string(), number())
16918
- }).optional()
16919
- });
16920
- const AuthOk = object({
16921
- daemonId: string().uuid(),
16922
- sessionEpoch: number().int(),
16923
- heartbeatSec: number().int(),
16924
- serverTime: string().datetime(),
16925
- resume: object({
16926
- accepted: boolean(),
16927
- redeliverFromSeq: record(string(), number()).optional()
16928
- }).optional()
16929
- });
16930
- //#endregion
16931
- //#region ../protocol/src/frames/route.ts
16932
- /**
16933
- * Routing & orchestration (C→D control) — protocol §5.
16934
- *
16935
- * `SessionKey` is the canonical session primitive shared across route/*,
16936
- * agent/*, and event/session. Its canonical string form is
16937
- * `${platform}:${channel}:${thread ?? "-"}`.
16938
- */
16939
- const Platform = _enum(["slack", "telegram"]);
16940
- const SessionKey = object({
16941
- platform: Platform,
16942
- channel: string(),
16943
- thread: string().optional()
16944
- });
16945
- /** Trigger-matching rule for a binding (protocol §5.1). */
16946
- const BindRule = object({ match: discriminatedUnion("kind", [
16947
- object({ kind: literal("mention") }),
16948
- object({ kind: literal("dm") }),
16949
- object({
16950
- kind: literal("keyword"),
16951
- value: string()
16952
- }),
16953
- object({ kind: literal("auto") })
16954
- ]) });
16955
- const RouteAssign = object({
16956
- sessionKey: SessionKey,
16957
- agentId: string().uuid(),
16958
- workspaceId: string().uuid(),
16959
- bindRules: array(BindRule).default([])
16960
- });
16961
- const RouteAssignAck = object({
16962
- ok: boolean(),
16963
- sessionKey: SessionKey,
16964
- reason: string().optional()
16965
- });
16966
- const RouteUpdate = object({
16967
- routingEpoch: number().int(),
16968
- rules: array(object({
16969
- match: unknown(),
16970
- agentId: string().uuid()
16971
- }))
16972
- });
16973
- /** Graceful scale-down / rebalance — protocol §5.3. */
16974
- const Drain = object({
16975
- scope: union([
16976
- object({
16977
- kind: literal("agent"),
16978
- agentId: string().uuid()
16979
- }),
16980
- object({ kind: literal("daemon") }),
16981
- object({
16982
- kind: literal("session"),
16983
- sessionKey: SessionKey
16984
- })
16985
- ]),
16986
- deadline: string().datetime()
16987
- });
16988
- const DrainProgress = object({
16989
- remaining: number().int(),
16990
- drained: array(SessionKey)
16991
- });
16992
- const DrainDone = object({ released: array(SessionKey) });
16993
- //#endregion
16994
- //#region ../protocol/src/frames/cron.ts
16995
- /**
16996
- * Cron sinks to the daemon (D5) — protocol §5.4.
16997
- *
16998
- * The CP owns the definition; the daemon owns firing + last-run persistence so
16999
- * crons fire even when the CP is down.
17000
- */
17001
- const CronUpsert = object({
17002
- cronId: string().uuid(),
17003
- schedule: string(),
17004
- target: object({ channel: string() }),
17005
- trigger: string(),
17006
- enabled: boolean().default(true)
17007
- });
17008
- const CronRemove = object({ cronId: string().uuid() });
17009
- //#endregion
17010
- //#region ../protocol/src/frames/secrets.ts
17011
- /**
17012
- * Secrets (C5 ↔ D10) — protocol §6.
17013
- *
17014
- * Lease-based, no plaintext on the wire or in PG. Every frame carries a
17015
- * REFERENCE to a Vault/KMS path, never the secret material itself.
17016
- */
17017
- const SecretsRequest = object({ scope: object({
17018
- platform: Platform,
17019
- workspaceId: string().uuid()
17020
- }) });
17021
- const SecretsGrant = object({
17022
- leaseId: string().uuid(),
17023
- scope: object({
17024
- platform: string(),
17025
- workspaceId: string().uuid()
17026
- }),
17027
- ref: string(),
17028
- ttl: number().int(),
17029
- renewBeforeSec: number().int()
17030
- });
17031
- const SecretsRenew = object({ leaseId: string().uuid() });
17032
- const SecretsRevoke = object({
17033
- leaseId: string().uuid(),
17034
- reason: string()
17035
- });
17036
- /** 🅼 Direct-to-store upload/download grant — protocol §3.2 / frame #25. */
17037
- const ScopeAttestation = object({
17038
- machineId: string().uuid(),
17039
- scope: _enum([
17040
- "attachment.put",
17041
- "attachment.get",
17042
- "facts.put"
17043
- ]),
17044
- resourceRef: string(),
17045
- jws: string(),
17046
- exp: string().datetime()
17047
- });
17048
- //#endregion
17049
- //#region ../protocol/src/frames/register.ts
17050
- /**
17051
- * Capability upload + the reconcile snapshot — protocol §3.3.
17052
- *
17053
- * `register/ok` is the authoritative source of truth: the daemon converges its
17054
- * local cache to it. CP wins all conflicts, so re-issuing the same snapshot is
17055
- * idempotent.
17056
- */
17057
- const RegisterReq = object({
17058
- host: string(),
17059
- capabilities: object({
17060
- platforms: array(Platform),
17061
- runtimes: array(string()),
17062
- acp: boolean(),
17063
- features: array(string()).default([])
17064
- }),
17065
- maxAgents: number().int(),
17066
- localState: object({
17067
- assignments: array(string()),
17068
- crons: array(string()),
17069
- leases: array(string())
17070
- })
17071
- });
17072
- const RegisterOk = object({
17073
- routingEpoch: number().int(),
17074
- assignments: array(RouteAssign),
17075
- crons: array(CronUpsert),
17076
- leases: array(SecretsGrant),
17077
- drop: object({
17078
- assignments: array(string()),
17079
- crons: array(string())
17080
- })
17081
- });
17082
- //#endregion
17083
- //#region ../protocol/src/frames/agent.ts
17084
- /**
17085
- * Agent lifecycle + delivery (protocol §4.3, §4.4, §7.4, §8).
17086
- *
17087
- * Body-locality invariant (protocol §12): no frame here carries
17088
- * `NormalizedMessage.text`. `agent/prompt` ships a `NormalizedMessageRef` — a
17089
- * reference the daemon resolves against its local body store.
17090
- */
17091
- /**
17092
- * A REFERENCE/digest of a normalized message, NOT the body (protocol §4.3).
17093
- * Enough for D4/D6 to fetch the local body and prompt.
17094
- */
17095
- const NormalizedMessageRef = object({
17096
- sessionKey: SessionKey,
17097
- platformMsgId: string(),
17098
- seenUpToSeq: number().int()
17099
- });
17100
- const AgentLaunch = object({
17101
- agentId: string().uuid(),
17102
- runtime: string(),
17103
- workspaceId: string().uuid(),
17104
- capabilities: array(string()),
17105
- mode: _enum(["long_lived", "per_turn"]).default("long_lived")
17106
- });
17107
- const AgentLaunched = object({
17108
- agentId: string().uuid(),
17109
- launchId: string().uuid(),
17110
- acpSessionId: string().optional(),
17111
- startedAt: string().datetime(),
17112
- runtime: string()
17113
- });
17114
- const AgentStop = object({
17115
- agentId: string().uuid(),
17116
- launchId: string().uuid(),
17117
- reason: string()
17118
- });
17119
- const AgentPrompt = object({
17120
- sessionKey: SessionKey,
17121
- agentId: string().uuid(),
17122
- content: NormalizedMessageRef,
17123
- seenUpToSeq: number().int()
17124
- });
17125
- const AgentPromptAck = object({
17126
- accepted: boolean(),
17127
- reason: _enum([
17128
- "queued",
17129
- "held",
17130
- "scope_denied",
17131
- "no_session",
17132
- "stale"
17133
- ]).optional(),
17134
- seq: number().int()
17135
- });
17136
- const AgentActivity = object({
17137
- agentId: string().uuid(),
17138
- launchId: string().uuid(),
17139
- state: _enum([
17140
- "thinking",
17141
- "tool_call",
17142
- "awaiting_permission",
17143
- "idle"
17144
- ]),
17145
- ts: string().datetime()
17146
- });
17147
- const AgentScopeDenied = object({
17148
- agentId: string().uuid(),
17149
- launchId: string().uuid(),
17150
- capability: string()
17151
- });
17152
- //#endregion
17153
- //#region ../protocol/src/frame.ts
17154
- /**
17155
- * The single source of truth for the wire: `type` string → payload zod schema.
17156
- *
17157
- * Mirrors the frame index in daemon-cp-ws-protocol.md §10 (the 29 numbered
17158
- * frames) plus the correlated REP types named in the "Reply" column
17159
- * (`route/assign/ack`, `drain/done`, and the generic `ack` replies) that also
17160
- * travel on the wire and must be decodable.
17161
- *
17162
- * `ws/codec.ts` validates every inbound `payload` against `FRAME_SCHEMAS[type]`;
17163
- * an unknown `type` → `ErrorFrame{code:"UNKNOWN_FRAME"}` (a REP, not a close).
17164
- */
17165
- const FRAME_SCHEMAS = {
17166
- auth: AuthReq,
17167
- "auth/ok": AuthOk,
17168
- register: RegisterReq,
17169
- "register/ok": RegisterOk,
17170
- heartbeat: object({
17171
- load: object({
17172
- cpu: number(),
17173
- mem: number(),
17174
- agents: number().int()
17175
- }),
17176
- health: _enum(["ok", "degraded"]),
17177
- activeSessions: number().int(),
17178
- degradedScopes: array(string()).default([])
17179
- }),
17180
- "agent/launch": AgentLaunch,
17181
- "agent/launched": AgentLaunched,
17182
- "agent/stop": AgentStop,
17183
- "agent/prompt": AgentPrompt,
17184
- "agent/prompt/ack": AgentPromptAck,
17185
- "agent/activity": AgentActivity,
17186
- "agent/scope-denied": AgentScopeDenied,
17187
- "route/assign": RouteAssign,
17188
- "route/assign/ack": RouteAssignAck,
17189
- "route/update": RouteUpdate,
17190
- "daemon/drain": Drain,
17191
- "drain/progress": DrainProgress,
17192
- "drain/done": DrainDone,
17193
- "cron/upsert": CronUpsert,
17194
- "cron/remove": CronRemove,
17195
- "secrets/request": SecretsRequest,
17196
- "secrets/grant": SecretsGrant,
17197
- "secrets/renew": SecretsRenew,
17198
- "secrets/revoke": SecretsRevoke,
17199
- "scope-attestation": ScopeAttestation,
17200
- "event/session": object({
17201
- sessionId: string().uuid(),
17202
- agentId: string().uuid(),
17203
- launchId: string().uuid(),
17204
- phase: _enum([
17205
- "start",
17206
- "plan",
17207
- "problem",
17208
- "end"
17209
- ]),
17210
- link: string().optional(),
17211
- summary: string().optional(),
17212
- ts: string().datetime()
17213
- }),
17214
- "facts/runtime-profile": object({
17215
- runtime: string(),
17216
- version: string(),
17217
- models: array(string()),
17218
- contextWindow: number().int().optional(),
17219
- acpSupport: _enum([
17220
- "full",
17221
- "partial",
17222
- "none"
17223
- ]),
17224
- toolCalling: boolean()
17225
- }),
17226
- "config/push": object({ keys: record(string(), unknown()) }),
17227
- "daemon/restart": object({
17228
- reason: string(),
17229
- drainFirst: boolean().default(true)
17230
- }),
17231
- "daemon/upgrade": object({
17232
- targetVersion: string(),
17233
- drainFirst: boolean().default(true)
17234
- }),
17235
- "daemon/control/ack": object({
17236
- accepted: boolean(),
17237
- willDrainUntil: string().datetime().optional()
17238
- }),
17239
- ack: object({
17240
- ok: boolean(),
17241
- reason: string().optional()
17242
- }),
17243
- error: object({
17244
- code: _enum([
17245
- "UNKNOWN_FRAME",
17246
- "FRAME_TOO_LARGE",
17247
- "PROTOCOL_STATE",
17248
- "BAD_PAYLOAD",
17249
- "AUTH_FAILED",
17250
- "ATTESTATION_INVALID",
17251
- "STALE_EPOCH",
17252
- "STALE_LAUNCH",
17253
- "SEQ_GAP",
17254
- "NO_SESSION",
17255
- "HELD",
17256
- "SCOPE_DENIED",
17257
- "LEASE_EXPIRED",
17258
- "LEASE_DENIED",
17259
- "RATE_LIMITED",
17260
- "INTERNAL"
17261
- ]),
17262
- message: string(),
17263
- retryable: boolean(),
17264
- details: record(string(), unknown()).optional()
17265
- })
17266
- };
17267
- Object.keys(FRAME_SCHEMAS);
17268
- /**
17269
- * Builds the envelope schema for one frame `type` with a `type` literal and the
17270
- * typed payload, so the discriminated union infers `payload` precisely.
17271
- */
17272
- function frame(type, payload) {
17273
- return object({
17274
- v: literal(1),
17275
- id: string().uuid(),
17276
- ts: string().datetime(),
17277
- type: literal(type),
17278
- corr: string().uuid().optional(),
17279
- payload
17280
- });
17281
- }
17282
- discriminatedUnion("type", [
17283
- frame("auth", FRAME_SCHEMAS["auth"]),
17284
- frame("auth/ok", FRAME_SCHEMAS["auth/ok"]),
17285
- frame("register", FRAME_SCHEMAS["register"]),
17286
- frame("register/ok", FRAME_SCHEMAS["register/ok"]),
17287
- frame("heartbeat", FRAME_SCHEMAS["heartbeat"]),
17288
- frame("agent/launch", FRAME_SCHEMAS["agent/launch"]),
17289
- frame("agent/launched", FRAME_SCHEMAS["agent/launched"]),
17290
- frame("agent/stop", FRAME_SCHEMAS["agent/stop"]),
17291
- frame("agent/prompt", FRAME_SCHEMAS["agent/prompt"]),
17292
- frame("agent/prompt/ack", FRAME_SCHEMAS["agent/prompt/ack"]),
17293
- frame("agent/activity", FRAME_SCHEMAS["agent/activity"]),
17294
- frame("agent/scope-denied", FRAME_SCHEMAS["agent/scope-denied"]),
17295
- frame("route/assign", FRAME_SCHEMAS["route/assign"]),
17296
- frame("route/assign/ack", FRAME_SCHEMAS["route/assign/ack"]),
17297
- frame("route/update", FRAME_SCHEMAS["route/update"]),
17298
- frame("daemon/drain", FRAME_SCHEMAS["daemon/drain"]),
17299
- frame("drain/progress", FRAME_SCHEMAS["drain/progress"]),
17300
- frame("drain/done", FRAME_SCHEMAS["drain/done"]),
17301
- frame("cron/upsert", FRAME_SCHEMAS["cron/upsert"]),
17302
- frame("cron/remove", FRAME_SCHEMAS["cron/remove"]),
17303
- frame("secrets/request", FRAME_SCHEMAS["secrets/request"]),
17304
- frame("secrets/grant", FRAME_SCHEMAS["secrets/grant"]),
17305
- frame("secrets/renew", FRAME_SCHEMAS["secrets/renew"]),
17306
- frame("secrets/revoke", FRAME_SCHEMAS["secrets/revoke"]),
17307
- frame("scope-attestation", FRAME_SCHEMAS["scope-attestation"]),
17308
- frame("event/session", FRAME_SCHEMAS["event/session"]),
17309
- frame("facts/runtime-profile", FRAME_SCHEMAS["facts/runtime-profile"]),
17310
- frame("config/push", FRAME_SCHEMAS["config/push"]),
17311
- frame("daemon/restart", FRAME_SCHEMAS["daemon/restart"]),
17312
- frame("daemon/upgrade", FRAME_SCHEMAS["daemon/upgrade"]),
17313
- frame("daemon/control/ack", FRAME_SCHEMAS["daemon/control/ack"]),
17314
- frame("ack", FRAME_SCHEMAS["ack"]),
17315
- frame("error", FRAME_SCHEMAS["error"])
17316
- ]);
17317
- //#endregion
17318
- //#region ../protocol/src/codec.ts
17319
- /** Soft cap per frame — 256 KiB (protocol §1). Over this → FRAME_TOO_LARGE. */
17320
- const MAX_FRAME_BYTES = 256 * 1024;
17321
- const textEncoder = new TextEncoder();
17322
- function byteLength(text) {
17323
- return textEncoder.encode(text).length;
17324
- }
17325
- function extractControlExt(json) {
17326
- if (typeof json !== "object" || json === null) return void 0;
17327
- const o = json;
17328
- const ext = {};
17329
- if (typeof o.epoch === "number") ext.epoch = o.epoch;
17330
- if (typeof o.seq === "number") ext.seq = o.seq;
17331
- if (typeof o.agentId === "string") ext.agentId = o.agentId;
17332
- if (typeof o.launchId === "string") ext.launchId = o.launchId;
17333
- return Object.keys(ext).length > 0 ? ext : void 0;
17334
- }
17335
- function decodeEnvelope(text) {
17336
- if (byteLength(text) > 262144) return {
17337
- ok: false,
17338
- id: NIL_UUID,
17339
- msg: "FRAME_TOO_LARGE"
17340
- };
17341
- let json;
17342
- try {
17343
- json = JSON.parse(text);
17344
- } catch {
17345
- return {
17346
- ok: false,
17347
- id: NIL_UUID,
17348
- msg: "invalid json"
17349
- };
17350
- }
17351
- const env = Envelope.safeParse(json);
17352
- if (!env.success) return {
17353
- ok: false,
17354
- id: typeof json === "object" && json !== null && typeof json.id === "string" ? json.id : NIL_UUID,
17355
- msg: env.error.message
17356
- };
17357
- const schema = FRAME_SCHEMAS[env.data.type];
17358
- if (!schema) return {
17359
- ok: false,
17360
- id: env.data.id,
17361
- msg: "UNKNOWN_FRAME"
17362
- };
17363
- const payload = schema.safeParse(env.data.payload);
17364
- if (!payload.success) return {
17365
- ok: false,
17366
- id: env.data.id,
17367
- msg: payload.error.message
17368
- };
17369
- const ext = extractControlExt(json);
17370
- return {
17371
- ok: true,
17372
- frame: {
17373
- ...env.data,
17374
- payload: payload.data
17375
- },
17376
- ...ext ? { ext } : {}
17377
- };
17378
- }
17379
- function buildEnvelope(type, payload, opts = {}) {
17380
- return {
17381
- v: 1,
17382
- id: opts.id ?? randomUUID(),
17383
- ts: opts.ts ?? (/* @__PURE__ */ new Date()).toISOString(),
17384
- type,
17385
- payload,
17386
- ...opts.corr ? { corr: opts.corr } : {},
17387
- ...opts.ext ?? {}
17388
- };
17389
- }
17390
- function encode(frame) {
17391
- return JSON.stringify(frame);
17392
- }
17393
- //#endregion
17394
- //#region ../protocol/src/index.ts
17395
- /**
17396
- * Narrowing guard factory: `isFrame("auth")(frame)` narrows a decoded
17397
- * `AnyFrame` to the member whose `type` matches.
17398
- */
17399
- function isFrame(type) {
17400
- return (frame) => frame.type === type;
17401
- }
17402
- //#endregion
17403
16875
  //#region ../../node_modules/.pnpm/ws@8.21.0/node_modules/ws/lib/constants.js
17404
16876
  var require_constants$1 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
17405
16877
  const BINARY_TYPES = [