@agentconnect.md/daemon 1.0.0-rc.22 → 1.0.0-rc.24
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 +668 -26
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -12,7 +12,6 @@ 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";
|
|
16
15
|
import { randomUUID } from "node:crypto";
|
|
17
16
|
import { lstat, open, readdir, realpath, stat as stat$1 } from "node:fs/promises";
|
|
18
17
|
import { DatabaseSync } from "node:sqlite";
|
|
@@ -16742,6 +16741,9 @@ var AcpHost = class {
|
|
|
16742
16741
|
opts;
|
|
16743
16742
|
child;
|
|
16744
16743
|
conn;
|
|
16744
|
+
live = /* @__PURE__ */ new Set();
|
|
16745
|
+
loadingSessions = /* @__PURE__ */ new Set();
|
|
16746
|
+
canLoad = false;
|
|
16745
16747
|
constructor(runtime, opts) {
|
|
16746
16748
|
this.runtime = runtime;
|
|
16747
16749
|
this.opts = opts;
|
|
@@ -16765,6 +16767,7 @@ var AcpHost = class {
|
|
|
16765
16767
|
const stream = ndJsonStream(Writable.toWeb(child.stdin), Readable.toWeb(child.stdout));
|
|
16766
16768
|
const self = this;
|
|
16767
16769
|
this.conn = client({ name: "agentconnect" }).onNotification(methods.client.session.update, (ctx) => {
|
|
16770
|
+
if (self.loadingSessions.has(ctx.params.sessionId)) return;
|
|
16768
16771
|
self.opts.onUpdate(ctx.params.sessionId, ctx.params.update);
|
|
16769
16772
|
}).onRequest(methods.client.session.requestPermission, (ctx) => {
|
|
16770
16773
|
const optionId = (ctx.params.options.find((o) => o.kind === "allow_once" || o.kind === "allow_always") ?? ctx.params.options[0])?.optionId;
|
|
@@ -16773,19 +16776,52 @@ var AcpHost = class {
|
|
|
16773
16776
|
optionId
|
|
16774
16777
|
} : { outcome: "cancelled" } };
|
|
16775
16778
|
}).connect(stream);
|
|
16776
|
-
await this.conn.agent.request(methods.agent.initialize, {
|
|
16779
|
+
const init = await this.conn.agent.request(methods.agent.initialize, {
|
|
16777
16780
|
protocolVersion: PROTOCOL_VERSION,
|
|
16778
16781
|
clientCapabilities: { fs: {
|
|
16779
16782
|
readTextFile: false,
|
|
16780
16783
|
writeTextFile: false
|
|
16781
16784
|
} }
|
|
16782
16785
|
});
|
|
16786
|
+
this.canLoad = init.agentCapabilities?.loadSession ?? false;
|
|
16787
|
+
this.opts.log?.debug(`acp: agent initialized (loadSession capability=${this.canLoad})`);
|
|
16783
16788
|
}
|
|
16784
16789
|
async newSession(cwd) {
|
|
16785
|
-
|
|
16790
|
+
const res = await this.conn.agent.request(methods.agent.session.new, {
|
|
16786
16791
|
cwd,
|
|
16787
16792
|
mcpServers: []
|
|
16788
|
-
})
|
|
16793
|
+
});
|
|
16794
|
+
this.live.add(res.sessionId);
|
|
16795
|
+
return res.sessionId;
|
|
16796
|
+
}
|
|
16797
|
+
/** True iff THIS agent process created or loaded `sessionId` in its current lifetime. */
|
|
16798
|
+
hasSession(sessionId) {
|
|
16799
|
+
return this.live.has(sessionId);
|
|
16800
|
+
}
|
|
16801
|
+
/** Whether the agent advertised the `loadSession` capability (session/load is usable). */
|
|
16802
|
+
loadSupported() {
|
|
16803
|
+
return this.canLoad;
|
|
16804
|
+
}
|
|
16805
|
+
/** Resume a previously-created session by id (ACP `session/load`). The agent
|
|
16806
|
+
* restores its own history server-side; its replayed session/update stream is
|
|
16807
|
+
* suppressed so it isn't re-posted. Throws if the agent can't load the id
|
|
16808
|
+
* (caller falls back to newSession). */
|
|
16809
|
+
async loadSession(sessionId, cwd) {
|
|
16810
|
+
this.loadingSessions.add(sessionId);
|
|
16811
|
+
try {
|
|
16812
|
+
await this.conn.agent.request(methods.agent.session.load, {
|
|
16813
|
+
sessionId,
|
|
16814
|
+
cwd,
|
|
16815
|
+
mcpServers: []
|
|
16816
|
+
});
|
|
16817
|
+
this.live.add(sessionId);
|
|
16818
|
+
this.opts.log?.info(`acp: resumed session ${sessionId} via session/load`);
|
|
16819
|
+
} catch (err) {
|
|
16820
|
+
this.opts.log?.debug(`acp: session/load failed for ${sessionId} (${err.message}) — will recreate`);
|
|
16821
|
+
throw err;
|
|
16822
|
+
} finally {
|
|
16823
|
+
this.loadingSessions.delete(sessionId);
|
|
16824
|
+
}
|
|
16789
16825
|
}
|
|
16790
16826
|
async prompt(sessionId, blocks) {
|
|
16791
16827
|
return (await this.conn.agent.request(methods.agent.session.prompt, {
|
|
@@ -16872,6 +16908,553 @@ async function runChat(opts) {
|
|
|
16872
16908
|
//#region src/version.ts
|
|
16873
16909
|
const DAEMON_VERSION = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version;
|
|
16874
16910
|
//#endregion
|
|
16911
|
+
//#region ../protocol/dist/envelope.js
|
|
16912
|
+
/**
|
|
16913
|
+
* The protocol envelope — every frame, both directions, is wrapped in this.
|
|
16914
|
+
* Mirrors daemon-cp-ws-protocol.md §1.1.
|
|
16915
|
+
*
|
|
16916
|
+
* `payload` is left as `unknown` here and validated by the per-type schema in
|
|
16917
|
+
* `frames/*` via `FRAME_SCHEMAS[type]` (see `frame.ts`). This two-step parse is
|
|
16918
|
+
* what lets the codec answer an unknown `type` with `UNKNOWN_FRAME` (a REP)
|
|
16919
|
+
* instead of a hard close — forward-compat, protocol §1.
|
|
16920
|
+
*/
|
|
16921
|
+
const Envelope = object({
|
|
16922
|
+
v: literal(1),
|
|
16923
|
+
id: string().uuid(),
|
|
16924
|
+
ts: string().datetime(),
|
|
16925
|
+
type: string(),
|
|
16926
|
+
corr: string().uuid().optional(),
|
|
16927
|
+
payload: unknown()
|
|
16928
|
+
});
|
|
16929
|
+
object({
|
|
16930
|
+
epoch: number().int(),
|
|
16931
|
+
seq: number().int().optional(),
|
|
16932
|
+
agentId: string().uuid().optional(),
|
|
16933
|
+
launchId: string().uuid().optional()
|
|
16934
|
+
});
|
|
16935
|
+
/** The all-zero UUID used when a frame is malformed past the point of reading `id`. */
|
|
16936
|
+
const NIL_UUID = "00000000-0000-0000-0000-000000000000";
|
|
16937
|
+
//#endregion
|
|
16938
|
+
//#region ../protocol/dist/frames/auth.js
|
|
16939
|
+
/**
|
|
16940
|
+
* Auth & identity — protocol §3.1 / §3.2.
|
|
16941
|
+
*
|
|
16942
|
+
* `auth` is the first frame after the socket opens. `auth/ok` carries the
|
|
16943
|
+
* minted `sessionEpoch` (the global fencing token), heartbeat cadence, and the
|
|
16944
|
+
* resume verdict.
|
|
16945
|
+
*/
|
|
16946
|
+
const AuthReq = object({
|
|
16947
|
+
apiKey: string(),
|
|
16948
|
+
daemonId: string().uuid().optional(),
|
|
16949
|
+
machineId: string().uuid().optional(),
|
|
16950
|
+
attestation: string().optional(),
|
|
16951
|
+
agentVersion: string(),
|
|
16952
|
+
resume: object({
|
|
16953
|
+
lastEpoch: number().int(),
|
|
16954
|
+
lastRecvSeq: record(string(), number())
|
|
16955
|
+
}).optional()
|
|
16956
|
+
});
|
|
16957
|
+
const AuthOk = object({
|
|
16958
|
+
daemonId: string().uuid(),
|
|
16959
|
+
sessionEpoch: number().int(),
|
|
16960
|
+
heartbeatSec: number().int(),
|
|
16961
|
+
serverTime: string().datetime(),
|
|
16962
|
+
resume: object({
|
|
16963
|
+
accepted: boolean(),
|
|
16964
|
+
redeliverFromSeq: record(string(), number()).optional()
|
|
16965
|
+
}).optional()
|
|
16966
|
+
});
|
|
16967
|
+
//#endregion
|
|
16968
|
+
//#region ../protocol/dist/frames/route.js
|
|
16969
|
+
/**
|
|
16970
|
+
* Routing & orchestration (C→D control) — protocol §5.
|
|
16971
|
+
*
|
|
16972
|
+
* `SessionKey` is the canonical session primitive shared across route/*,
|
|
16973
|
+
* agent/*, and event/session. Its canonical string form is
|
|
16974
|
+
* `${platform}:${channel}:${thread ?? "-"}`.
|
|
16975
|
+
*/
|
|
16976
|
+
const Platform = _enum(["slack", "telegram"]);
|
|
16977
|
+
const SessionKey = object({
|
|
16978
|
+
platform: Platform,
|
|
16979
|
+
channel: string(),
|
|
16980
|
+
thread: string().optional()
|
|
16981
|
+
});
|
|
16982
|
+
/** Trigger-matching rule for a binding (protocol §5.1). */
|
|
16983
|
+
const BindRule = object({ match: discriminatedUnion("kind", [
|
|
16984
|
+
object({ kind: literal("mention") }),
|
|
16985
|
+
object({ kind: literal("dm") }),
|
|
16986
|
+
object({
|
|
16987
|
+
kind: literal("keyword"),
|
|
16988
|
+
value: string()
|
|
16989
|
+
}),
|
|
16990
|
+
object({ kind: literal("auto") })
|
|
16991
|
+
]) });
|
|
16992
|
+
const RouteAssign = object({
|
|
16993
|
+
sessionKey: SessionKey,
|
|
16994
|
+
agentId: string().uuid(),
|
|
16995
|
+
workspaceId: string().uuid(),
|
|
16996
|
+
bindRules: array(BindRule).default([])
|
|
16997
|
+
});
|
|
16998
|
+
const RouteAssignAck = object({
|
|
16999
|
+
ok: boolean(),
|
|
17000
|
+
sessionKey: SessionKey,
|
|
17001
|
+
reason: string().optional()
|
|
17002
|
+
});
|
|
17003
|
+
const RouteUpdate = object({
|
|
17004
|
+
routingEpoch: number().int(),
|
|
17005
|
+
rules: array(object({
|
|
17006
|
+
match: unknown(),
|
|
17007
|
+
agentId: string().uuid()
|
|
17008
|
+
}))
|
|
17009
|
+
});
|
|
17010
|
+
/** Graceful scale-down / rebalance — protocol §5.3. */
|
|
17011
|
+
const Drain = object({
|
|
17012
|
+
scope: union([
|
|
17013
|
+
object({
|
|
17014
|
+
kind: literal("agent"),
|
|
17015
|
+
agentId: string().uuid()
|
|
17016
|
+
}),
|
|
17017
|
+
object({ kind: literal("daemon") }),
|
|
17018
|
+
object({
|
|
17019
|
+
kind: literal("session"),
|
|
17020
|
+
sessionKey: SessionKey
|
|
17021
|
+
})
|
|
17022
|
+
]),
|
|
17023
|
+
deadline: string().datetime()
|
|
17024
|
+
});
|
|
17025
|
+
const DrainProgress = object({
|
|
17026
|
+
remaining: number().int(),
|
|
17027
|
+
drained: array(SessionKey)
|
|
17028
|
+
});
|
|
17029
|
+
const DrainDone = object({ released: array(SessionKey) });
|
|
17030
|
+
//#endregion
|
|
17031
|
+
//#region ../protocol/dist/frames/cron.js
|
|
17032
|
+
/**
|
|
17033
|
+
* Cron sinks to the daemon (D5) — protocol §5.4.
|
|
17034
|
+
*
|
|
17035
|
+
* The CP owns the definition; the daemon owns firing + last-run persistence so
|
|
17036
|
+
* crons fire even when the CP is down.
|
|
17037
|
+
*/
|
|
17038
|
+
const CronUpsert = object({
|
|
17039
|
+
cronId: string().uuid(),
|
|
17040
|
+
schedule: string(),
|
|
17041
|
+
target: object({ channel: string() }),
|
|
17042
|
+
trigger: string(),
|
|
17043
|
+
enabled: boolean().default(true)
|
|
17044
|
+
});
|
|
17045
|
+
const CronRemove = object({ cronId: string().uuid() });
|
|
17046
|
+
//#endregion
|
|
17047
|
+
//#region ../protocol/dist/frames/secrets.js
|
|
17048
|
+
/**
|
|
17049
|
+
* Secrets (C5 ↔ D10) — protocol §6.
|
|
17050
|
+
*
|
|
17051
|
+
* Lease-based, no plaintext on the wire or in PG. Every frame carries a
|
|
17052
|
+
* REFERENCE to a Vault/KMS path, never the secret material itself.
|
|
17053
|
+
*/
|
|
17054
|
+
const SecretsRequest = object({ scope: object({
|
|
17055
|
+
platform: Platform,
|
|
17056
|
+
workspaceId: string().uuid()
|
|
17057
|
+
}) });
|
|
17058
|
+
const SecretsGrant = object({
|
|
17059
|
+
leaseId: string().uuid(),
|
|
17060
|
+
scope: object({
|
|
17061
|
+
platform: string(),
|
|
17062
|
+
workspaceId: string().uuid()
|
|
17063
|
+
}),
|
|
17064
|
+
ref: string(),
|
|
17065
|
+
ttl: number().int(),
|
|
17066
|
+
renewBeforeSec: number().int()
|
|
17067
|
+
});
|
|
17068
|
+
const SecretsRenew = object({ leaseId: string().uuid() });
|
|
17069
|
+
const SecretsRevoke = object({
|
|
17070
|
+
leaseId: string().uuid(),
|
|
17071
|
+
reason: string()
|
|
17072
|
+
});
|
|
17073
|
+
/** 🅼 Direct-to-store upload/download grant — protocol §3.2 / frame #25. */
|
|
17074
|
+
const ScopeAttestation = object({
|
|
17075
|
+
machineId: string().uuid(),
|
|
17076
|
+
scope: _enum([
|
|
17077
|
+
"attachment.put",
|
|
17078
|
+
"attachment.get",
|
|
17079
|
+
"facts.put"
|
|
17080
|
+
]),
|
|
17081
|
+
resourceRef: string(),
|
|
17082
|
+
jws: string(),
|
|
17083
|
+
exp: string().datetime()
|
|
17084
|
+
});
|
|
17085
|
+
//#endregion
|
|
17086
|
+
//#region ../protocol/dist/frames/agent.js
|
|
17087
|
+
/**
|
|
17088
|
+
* Agent lifecycle + delivery (protocol §4.3, §4.4, §7.4, §8).
|
|
17089
|
+
*
|
|
17090
|
+
* Body-locality invariant (protocol §12): no frame here carries
|
|
17091
|
+
* `NormalizedMessage.text`. `agent/prompt` ships a `NormalizedMessageRef` — a
|
|
17092
|
+
* reference the daemon resolves against its local body store.
|
|
17093
|
+
*/
|
|
17094
|
+
/**
|
|
17095
|
+
* A REFERENCE/digest of a normalized message, NOT the body (protocol §4.3).
|
|
17096
|
+
* Enough for D4/D6 to fetch the local body and prompt.
|
|
17097
|
+
*/
|
|
17098
|
+
const NormalizedMessageRef = object({
|
|
17099
|
+
sessionKey: SessionKey,
|
|
17100
|
+
platformMsgId: string(),
|
|
17101
|
+
seenUpToSeq: number().int()
|
|
17102
|
+
});
|
|
17103
|
+
/**
|
|
17104
|
+
* The editable agent definition the CP owns and the daemon needs to run it:
|
|
17105
|
+
* prompt + runtime selection. Raft ships this in the launch config and the
|
|
17106
|
+
* daemon synthesizes the system prompt locally; `description` IS the prompt.
|
|
17107
|
+
*/
|
|
17108
|
+
const AgentSpec = object({
|
|
17109
|
+
name: string(),
|
|
17110
|
+
description: string().optional(),
|
|
17111
|
+
model: string().optional(),
|
|
17112
|
+
reasoningEffort: string().optional(),
|
|
17113
|
+
executionMode: string().optional(),
|
|
17114
|
+
env: record(string(), string()).optional()
|
|
17115
|
+
});
|
|
17116
|
+
const AgentLaunch = object({
|
|
17117
|
+
agentId: string().uuid(),
|
|
17118
|
+
runtime: string(),
|
|
17119
|
+
workspaceId: string().uuid(),
|
|
17120
|
+
capabilities: array(string()),
|
|
17121
|
+
spec: AgentSpec,
|
|
17122
|
+
mode: _enum(["long_lived", "per_turn"]).default("long_lived")
|
|
17123
|
+
});
|
|
17124
|
+
/**
|
|
17125
|
+
* Live agent CRUD (C→D): the console edited an agent's spec; push it so a
|
|
17126
|
+
* running daemon reloads without waiting for the next launch. `agent/remove`
|
|
17127
|
+
* tears the agent down. Deleting an agent never relaunches it.
|
|
17128
|
+
*/
|
|
17129
|
+
const AgentUpsert = object({
|
|
17130
|
+
agentId: string().uuid(),
|
|
17131
|
+
spec: AgentSpec
|
|
17132
|
+
});
|
|
17133
|
+
const AgentRemove = object({ agentId: string().uuid() });
|
|
17134
|
+
const AgentLaunched = object({
|
|
17135
|
+
agentId: string().uuid(),
|
|
17136
|
+
launchId: string().uuid(),
|
|
17137
|
+
acpSessionId: string().optional(),
|
|
17138
|
+
startedAt: string().datetime(),
|
|
17139
|
+
runtime: string()
|
|
17140
|
+
});
|
|
17141
|
+
const AgentStop = object({
|
|
17142
|
+
agentId: string().uuid(),
|
|
17143
|
+
launchId: string().uuid(),
|
|
17144
|
+
reason: string()
|
|
17145
|
+
});
|
|
17146
|
+
const AgentPrompt = object({
|
|
17147
|
+
sessionKey: SessionKey,
|
|
17148
|
+
agentId: string().uuid(),
|
|
17149
|
+
content: NormalizedMessageRef,
|
|
17150
|
+
seenUpToSeq: number().int()
|
|
17151
|
+
});
|
|
17152
|
+
const AgentPromptAck = object({
|
|
17153
|
+
accepted: boolean(),
|
|
17154
|
+
reason: _enum([
|
|
17155
|
+
"queued",
|
|
17156
|
+
"held",
|
|
17157
|
+
"scope_denied",
|
|
17158
|
+
"no_session",
|
|
17159
|
+
"stale"
|
|
17160
|
+
]).optional(),
|
|
17161
|
+
seq: number().int()
|
|
17162
|
+
});
|
|
17163
|
+
const AgentActivity = object({
|
|
17164
|
+
agentId: string().uuid(),
|
|
17165
|
+
launchId: string().uuid(),
|
|
17166
|
+
state: _enum([
|
|
17167
|
+
"thinking",
|
|
17168
|
+
"tool_call",
|
|
17169
|
+
"awaiting_permission",
|
|
17170
|
+
"idle"
|
|
17171
|
+
]),
|
|
17172
|
+
ts: string().datetime()
|
|
17173
|
+
});
|
|
17174
|
+
const AgentScopeDenied = object({
|
|
17175
|
+
agentId: string().uuid(),
|
|
17176
|
+
launchId: string().uuid(),
|
|
17177
|
+
capability: string()
|
|
17178
|
+
});
|
|
17179
|
+
//#endregion
|
|
17180
|
+
//#region ../protocol/dist/frame.js
|
|
17181
|
+
/**
|
|
17182
|
+
* The single source of truth for the wire: `type` string → payload zod schema.
|
|
17183
|
+
*
|
|
17184
|
+
* Mirrors the frame index in daemon-cp-ws-protocol.md §10 (the 29 numbered
|
|
17185
|
+
* frames) plus the correlated REP types named in the "Reply" column
|
|
17186
|
+
* (`route/assign/ack`, `drain/done`, and the generic `ack` replies) that also
|
|
17187
|
+
* travel on the wire and must be decodable.
|
|
17188
|
+
*
|
|
17189
|
+
* `ws/codec.ts` validates every inbound `payload` against `FRAME_SCHEMAS[type]`;
|
|
17190
|
+
* an unknown `type` → `ErrorFrame{code:"UNKNOWN_FRAME"}` (a REP, not a close).
|
|
17191
|
+
*/
|
|
17192
|
+
const FRAME_SCHEMAS = {
|
|
17193
|
+
auth: AuthReq,
|
|
17194
|
+
"auth/ok": AuthOk,
|
|
17195
|
+
register: object({
|
|
17196
|
+
host: string(),
|
|
17197
|
+
capabilities: object({
|
|
17198
|
+
platforms: array(Platform),
|
|
17199
|
+
runtimes: array(string()),
|
|
17200
|
+
acp: boolean(),
|
|
17201
|
+
features: array(string()).default([])
|
|
17202
|
+
}),
|
|
17203
|
+
maxAgents: number().int(),
|
|
17204
|
+
localState: object({
|
|
17205
|
+
assignments: array(string()),
|
|
17206
|
+
crons: array(string()),
|
|
17207
|
+
leases: array(string())
|
|
17208
|
+
})
|
|
17209
|
+
}),
|
|
17210
|
+
"register/ok": object({
|
|
17211
|
+
routingEpoch: number().int(),
|
|
17212
|
+
assignments: array(RouteAssign),
|
|
17213
|
+
agents: array(AgentSpec.extend({ agentId: string().uuid() })).default([]),
|
|
17214
|
+
crons: array(CronUpsert),
|
|
17215
|
+
leases: array(SecretsGrant),
|
|
17216
|
+
drop: object({
|
|
17217
|
+
assignments: array(string()),
|
|
17218
|
+
crons: array(string())
|
|
17219
|
+
})
|
|
17220
|
+
}),
|
|
17221
|
+
heartbeat: object({
|
|
17222
|
+
load: object({
|
|
17223
|
+
cpu: number(),
|
|
17224
|
+
mem: number(),
|
|
17225
|
+
agents: number().int()
|
|
17226
|
+
}),
|
|
17227
|
+
health: _enum(["ok", "degraded"]),
|
|
17228
|
+
activeSessions: number().int(),
|
|
17229
|
+
degradedScopes: array(string()).default([])
|
|
17230
|
+
}),
|
|
17231
|
+
"agent/launch": AgentLaunch,
|
|
17232
|
+
"agent/launched": AgentLaunched,
|
|
17233
|
+
"agent/stop": AgentStop,
|
|
17234
|
+
"agent/upsert": AgentUpsert,
|
|
17235
|
+
"agent/remove": AgentRemove,
|
|
17236
|
+
"agent/prompt": AgentPrompt,
|
|
17237
|
+
"agent/prompt/ack": AgentPromptAck,
|
|
17238
|
+
"agent/activity": AgentActivity,
|
|
17239
|
+
"agent/scope-denied": AgentScopeDenied,
|
|
17240
|
+
"route/assign": RouteAssign,
|
|
17241
|
+
"route/assign/ack": RouteAssignAck,
|
|
17242
|
+
"route/update": RouteUpdate,
|
|
17243
|
+
"daemon/drain": Drain,
|
|
17244
|
+
"drain/progress": DrainProgress,
|
|
17245
|
+
"drain/done": DrainDone,
|
|
17246
|
+
"cron/upsert": CronUpsert,
|
|
17247
|
+
"cron/remove": CronRemove,
|
|
17248
|
+
"secrets/request": SecretsRequest,
|
|
17249
|
+
"secrets/grant": SecretsGrant,
|
|
17250
|
+
"secrets/renew": SecretsRenew,
|
|
17251
|
+
"secrets/revoke": SecretsRevoke,
|
|
17252
|
+
"scope-attestation": ScopeAttestation,
|
|
17253
|
+
"event/session": object({
|
|
17254
|
+
sessionId: string().uuid(),
|
|
17255
|
+
agentId: string().uuid(),
|
|
17256
|
+
launchId: string().uuid(),
|
|
17257
|
+
phase: _enum([
|
|
17258
|
+
"start",
|
|
17259
|
+
"plan",
|
|
17260
|
+
"problem",
|
|
17261
|
+
"end"
|
|
17262
|
+
]),
|
|
17263
|
+
link: string().optional(),
|
|
17264
|
+
summary: string().optional(),
|
|
17265
|
+
ts: string().datetime()
|
|
17266
|
+
}),
|
|
17267
|
+
"facts/runtime-profile": object({
|
|
17268
|
+
runtime: string(),
|
|
17269
|
+
version: string(),
|
|
17270
|
+
models: array(string()),
|
|
17271
|
+
contextWindow: number().int().optional(),
|
|
17272
|
+
acpSupport: _enum([
|
|
17273
|
+
"full",
|
|
17274
|
+
"partial",
|
|
17275
|
+
"none"
|
|
17276
|
+
]),
|
|
17277
|
+
toolCalling: boolean()
|
|
17278
|
+
}),
|
|
17279
|
+
"config/push": object({ keys: record(string(), unknown()) }),
|
|
17280
|
+
"daemon/restart": object({
|
|
17281
|
+
reason: string(),
|
|
17282
|
+
drainFirst: boolean().default(true)
|
|
17283
|
+
}),
|
|
17284
|
+
"daemon/upgrade": object({
|
|
17285
|
+
targetVersion: string(),
|
|
17286
|
+
drainFirst: boolean().default(true)
|
|
17287
|
+
}),
|
|
17288
|
+
"daemon/control/ack": object({
|
|
17289
|
+
accepted: boolean(),
|
|
17290
|
+
willDrainUntil: string().datetime().optional()
|
|
17291
|
+
}),
|
|
17292
|
+
ack: object({
|
|
17293
|
+
ok: boolean(),
|
|
17294
|
+
reason: string().optional()
|
|
17295
|
+
}),
|
|
17296
|
+
error: object({
|
|
17297
|
+
code: _enum([
|
|
17298
|
+
"UNKNOWN_FRAME",
|
|
17299
|
+
"FRAME_TOO_LARGE",
|
|
17300
|
+
"PROTOCOL_STATE",
|
|
17301
|
+
"BAD_PAYLOAD",
|
|
17302
|
+
"AUTH_FAILED",
|
|
17303
|
+
"ATTESTATION_INVALID",
|
|
17304
|
+
"STALE_EPOCH",
|
|
17305
|
+
"STALE_LAUNCH",
|
|
17306
|
+
"SEQ_GAP",
|
|
17307
|
+
"NO_SESSION",
|
|
17308
|
+
"HELD",
|
|
17309
|
+
"SCOPE_DENIED",
|
|
17310
|
+
"LEASE_EXPIRED",
|
|
17311
|
+
"LEASE_DENIED",
|
|
17312
|
+
"RATE_LIMITED",
|
|
17313
|
+
"INTERNAL"
|
|
17314
|
+
]),
|
|
17315
|
+
message: string(),
|
|
17316
|
+
retryable: boolean(),
|
|
17317
|
+
details: record(string(), unknown()).optional()
|
|
17318
|
+
})
|
|
17319
|
+
};
|
|
17320
|
+
Object.keys(FRAME_SCHEMAS);
|
|
17321
|
+
/**
|
|
17322
|
+
* Builds the envelope schema for one frame `type` with a `type` literal and the
|
|
17323
|
+
* typed payload, so the discriminated union infers `payload` precisely.
|
|
17324
|
+
*/
|
|
17325
|
+
function frame(type, payload) {
|
|
17326
|
+
return object({
|
|
17327
|
+
v: literal(1),
|
|
17328
|
+
id: string().uuid(),
|
|
17329
|
+
ts: string().datetime(),
|
|
17330
|
+
type: literal(type),
|
|
17331
|
+
corr: string().uuid().optional(),
|
|
17332
|
+
payload
|
|
17333
|
+
});
|
|
17334
|
+
}
|
|
17335
|
+
discriminatedUnion("type", [
|
|
17336
|
+
frame("auth", FRAME_SCHEMAS["auth"]),
|
|
17337
|
+
frame("auth/ok", FRAME_SCHEMAS["auth/ok"]),
|
|
17338
|
+
frame("register", FRAME_SCHEMAS["register"]),
|
|
17339
|
+
frame("register/ok", FRAME_SCHEMAS["register/ok"]),
|
|
17340
|
+
frame("heartbeat", FRAME_SCHEMAS["heartbeat"]),
|
|
17341
|
+
frame("agent/launch", FRAME_SCHEMAS["agent/launch"]),
|
|
17342
|
+
frame("agent/launched", FRAME_SCHEMAS["agent/launched"]),
|
|
17343
|
+
frame("agent/stop", FRAME_SCHEMAS["agent/stop"]),
|
|
17344
|
+
frame("agent/upsert", FRAME_SCHEMAS["agent/upsert"]),
|
|
17345
|
+
frame("agent/remove", FRAME_SCHEMAS["agent/remove"]),
|
|
17346
|
+
frame("agent/prompt", FRAME_SCHEMAS["agent/prompt"]),
|
|
17347
|
+
frame("agent/prompt/ack", FRAME_SCHEMAS["agent/prompt/ack"]),
|
|
17348
|
+
frame("agent/activity", FRAME_SCHEMAS["agent/activity"]),
|
|
17349
|
+
frame("agent/scope-denied", FRAME_SCHEMAS["agent/scope-denied"]),
|
|
17350
|
+
frame("route/assign", FRAME_SCHEMAS["route/assign"]),
|
|
17351
|
+
frame("route/assign/ack", FRAME_SCHEMAS["route/assign/ack"]),
|
|
17352
|
+
frame("route/update", FRAME_SCHEMAS["route/update"]),
|
|
17353
|
+
frame("daemon/drain", FRAME_SCHEMAS["daemon/drain"]),
|
|
17354
|
+
frame("drain/progress", FRAME_SCHEMAS["drain/progress"]),
|
|
17355
|
+
frame("drain/done", FRAME_SCHEMAS["drain/done"]),
|
|
17356
|
+
frame("cron/upsert", FRAME_SCHEMAS["cron/upsert"]),
|
|
17357
|
+
frame("cron/remove", FRAME_SCHEMAS["cron/remove"]),
|
|
17358
|
+
frame("secrets/request", FRAME_SCHEMAS["secrets/request"]),
|
|
17359
|
+
frame("secrets/grant", FRAME_SCHEMAS["secrets/grant"]),
|
|
17360
|
+
frame("secrets/renew", FRAME_SCHEMAS["secrets/renew"]),
|
|
17361
|
+
frame("secrets/revoke", FRAME_SCHEMAS["secrets/revoke"]),
|
|
17362
|
+
frame("scope-attestation", FRAME_SCHEMAS["scope-attestation"]),
|
|
17363
|
+
frame("event/session", FRAME_SCHEMAS["event/session"]),
|
|
17364
|
+
frame("facts/runtime-profile", FRAME_SCHEMAS["facts/runtime-profile"]),
|
|
17365
|
+
frame("config/push", FRAME_SCHEMAS["config/push"]),
|
|
17366
|
+
frame("daemon/restart", FRAME_SCHEMAS["daemon/restart"]),
|
|
17367
|
+
frame("daemon/upgrade", FRAME_SCHEMAS["daemon/upgrade"]),
|
|
17368
|
+
frame("daemon/control/ack", FRAME_SCHEMAS["daemon/control/ack"]),
|
|
17369
|
+
frame("ack", FRAME_SCHEMAS["ack"]),
|
|
17370
|
+
frame("error", FRAME_SCHEMAS["error"])
|
|
17371
|
+
]);
|
|
17372
|
+
//#endregion
|
|
17373
|
+
//#region ../protocol/dist/codec.js
|
|
17374
|
+
/** Soft cap per frame — 256 KiB (protocol §1). Over this → FRAME_TOO_LARGE. */
|
|
17375
|
+
const MAX_FRAME_BYTES = 256 * 1024;
|
|
17376
|
+
const textEncoder = new TextEncoder();
|
|
17377
|
+
function byteLength(text) {
|
|
17378
|
+
return textEncoder.encode(text).length;
|
|
17379
|
+
}
|
|
17380
|
+
function extractControlExt(json) {
|
|
17381
|
+
if (typeof json !== "object" || json === null) return void 0;
|
|
17382
|
+
const o = json;
|
|
17383
|
+
const ext = {};
|
|
17384
|
+
if (typeof o.epoch === "number") ext.epoch = o.epoch;
|
|
17385
|
+
if (typeof o.seq === "number") ext.seq = o.seq;
|
|
17386
|
+
if (typeof o.agentId === "string") ext.agentId = o.agentId;
|
|
17387
|
+
if (typeof o.launchId === "string") ext.launchId = o.launchId;
|
|
17388
|
+
return Object.keys(ext).length > 0 ? ext : void 0;
|
|
17389
|
+
}
|
|
17390
|
+
function decodeEnvelope(text) {
|
|
17391
|
+
if (byteLength(text) > 262144) return {
|
|
17392
|
+
ok: false,
|
|
17393
|
+
id: NIL_UUID,
|
|
17394
|
+
msg: "FRAME_TOO_LARGE"
|
|
17395
|
+
};
|
|
17396
|
+
let json;
|
|
17397
|
+
try {
|
|
17398
|
+
json = JSON.parse(text);
|
|
17399
|
+
} catch {
|
|
17400
|
+
return {
|
|
17401
|
+
ok: false,
|
|
17402
|
+
id: NIL_UUID,
|
|
17403
|
+
msg: "invalid json"
|
|
17404
|
+
};
|
|
17405
|
+
}
|
|
17406
|
+
const env = Envelope.safeParse(json);
|
|
17407
|
+
if (!env.success) return {
|
|
17408
|
+
ok: false,
|
|
17409
|
+
id: typeof json === "object" && json !== null && typeof json.id === "string" ? json.id : NIL_UUID,
|
|
17410
|
+
msg: env.error.message
|
|
17411
|
+
};
|
|
17412
|
+
const schema = FRAME_SCHEMAS[env.data.type];
|
|
17413
|
+
if (!schema) return {
|
|
17414
|
+
ok: false,
|
|
17415
|
+
id: env.data.id,
|
|
17416
|
+
msg: "UNKNOWN_FRAME"
|
|
17417
|
+
};
|
|
17418
|
+
const payload = schema.safeParse(env.data.payload);
|
|
17419
|
+
if (!payload.success) return {
|
|
17420
|
+
ok: false,
|
|
17421
|
+
id: env.data.id,
|
|
17422
|
+
msg: payload.error.message
|
|
17423
|
+
};
|
|
17424
|
+
const ext = extractControlExt(json);
|
|
17425
|
+
return {
|
|
17426
|
+
ok: true,
|
|
17427
|
+
frame: {
|
|
17428
|
+
...env.data,
|
|
17429
|
+
payload: payload.data
|
|
17430
|
+
},
|
|
17431
|
+
...ext ? { ext } : {}
|
|
17432
|
+
};
|
|
17433
|
+
}
|
|
17434
|
+
function buildEnvelope(type, payload, opts = {}) {
|
|
17435
|
+
return {
|
|
17436
|
+
v: 1,
|
|
17437
|
+
id: opts.id ?? randomUUID(),
|
|
17438
|
+
ts: opts.ts ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
17439
|
+
type,
|
|
17440
|
+
payload,
|
|
17441
|
+
...opts.corr ? { corr: opts.corr } : {},
|
|
17442
|
+
...opts.ext ?? {}
|
|
17443
|
+
};
|
|
17444
|
+
}
|
|
17445
|
+
function encode(frame) {
|
|
17446
|
+
return JSON.stringify(frame);
|
|
17447
|
+
}
|
|
17448
|
+
//#endregion
|
|
17449
|
+
//#region ../protocol/dist/index.js
|
|
17450
|
+
/**
|
|
17451
|
+
* Narrowing guard factory: `isFrame("auth")(frame)` narrows a decoded
|
|
17452
|
+
* `AnyFrame` to the member whose `type` matches.
|
|
17453
|
+
*/
|
|
17454
|
+
function isFrame(type) {
|
|
17455
|
+
return (frame) => frame.type === type;
|
|
17456
|
+
}
|
|
17457
|
+
//#endregion
|
|
16875
17458
|
//#region ../../node_modules/.pnpm/ws@8.21.0/node_modules/ws/lib/constants.js
|
|
16876
17459
|
var require_constants$1 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
16877
17460
|
const BINARY_TYPES = [
|
|
@@ -20594,7 +21177,7 @@ const defaultExec = (cmd, args) => new Promise((resolve) => {
|
|
|
20594
21177
|
/** macOS launchd controller. Writes a LaunchAgent plist that runs
|
|
20595
21178
|
* `<node> <cli-entry> run`, and drives it with `launchctl bootstrap/bootout`
|
|
20596
21179
|
* (falling back to legacy `load/unload` on older macOS). */
|
|
20597
|
-
const LABEL = "
|
|
21180
|
+
const LABEL = "md.agentconnect.daemon";
|
|
20598
21181
|
function buildPlist(a) {
|
|
20599
21182
|
const env = a.includeRootEnv ? ` <key>EnvironmentVariables</key>\n <dict>\n <key>AGENTCONNECT_ROOT</key>\n <string>${a.root}</string>\n </dict>\n` : "";
|
|
20600
21183
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
@@ -22403,11 +22986,36 @@ function watch$1(paths, options = {}) {
|
|
|
22403
22986
|
}
|
|
22404
22987
|
//#endregion
|
|
22405
22988
|
//#region src/reconciler/reconciler.ts
|
|
22406
|
-
|
|
22989
|
+
/** Stable identity-free signature of an agent's effective config. Excludes the
|
|
22990
|
+
* loader-only `dir`/`env` fields (present on `LoadedAgent`) so the comparison is
|
|
22991
|
+
* over the agent's behaviour, not where it was found. Both sides are the LOADED
|
|
22992
|
+
* form (interpolated, path-absolutized), so this is apples-to-apples and stable
|
|
22993
|
+
* across re-serialization (zod key order is fixed by the schema). */
|
|
22994
|
+
function signature(a) {
|
|
22995
|
+
const { dir, env, ...rest } = a;
|
|
22996
|
+
return JSON.stringify(rest);
|
|
22997
|
+
}
|
|
22998
|
+
/**
|
|
22999
|
+
* Diff desired (freshly loaded active agents) against the running set.
|
|
23000
|
+
* - `toStart` — desired ids not currently running.
|
|
23001
|
+
* - `toStop` — running ids no longer desired.
|
|
23002
|
+
* - `toRestart` — same id, changed config (runtime/workspace/integrations/…): the
|
|
23003
|
+
* host must be evicted so the next session picks up fresh config (design §5.2).
|
|
23004
|
+
* Without this, an in-place edit to an existing agent.json is silently a no-op.
|
|
23005
|
+
*/
|
|
23006
|
+
function diffAgents(desired, actual) {
|
|
22407
23007
|
const desiredIds = new Set(desired.map((a) => a.id));
|
|
23008
|
+
const toStart = [];
|
|
23009
|
+
const toRestart = [];
|
|
23010
|
+
for (const a of desired) {
|
|
23011
|
+
const cur = actual.get(a.id);
|
|
23012
|
+
if (!cur) toStart.push(a);
|
|
23013
|
+
else if (signature(cur) !== signature(a)) toRestart.push(a);
|
|
23014
|
+
}
|
|
22408
23015
|
return {
|
|
22409
|
-
toStart
|
|
22410
|
-
toStop:
|
|
23016
|
+
toStart,
|
|
23017
|
+
toStop: [...actual.keys()].filter((id) => !desiredIds.has(id)),
|
|
23018
|
+
toRestart
|
|
22411
23019
|
};
|
|
22412
23020
|
}
|
|
22413
23021
|
//#endregion
|
|
@@ -22511,6 +23119,24 @@ var SessionManager = class {
|
|
|
22511
23119
|
updatedAt: Date.now()
|
|
22512
23120
|
};
|
|
22513
23121
|
this.deps.store.upsertSession(rec);
|
|
23122
|
+
} else if (host.hasSession?.(rec.acpSessionId) === false) {
|
|
23123
|
+
const cwd = await prepareWorkspace(agent);
|
|
23124
|
+
let resumed = false;
|
|
23125
|
+
if (host.loadSupported?.()) try {
|
|
23126
|
+
await host.loadSession(rec.acpSessionId, cwd);
|
|
23127
|
+
resumed = true;
|
|
23128
|
+
} catch {}
|
|
23129
|
+
if (!resumed) {
|
|
23130
|
+
const acpSessionId = await host.newSession(cwd);
|
|
23131
|
+
rec = {
|
|
23132
|
+
...rec,
|
|
23133
|
+
acpSessionId,
|
|
23134
|
+
state: "idle",
|
|
23135
|
+
lastDeliveredTs: null,
|
|
23136
|
+
updatedAt: Date.now()
|
|
23137
|
+
};
|
|
23138
|
+
this.deps.store.upsertSession(rec);
|
|
23139
|
+
}
|
|
22514
23140
|
}
|
|
22515
23141
|
const gap = this.deps.store.transcriptSince(msg.channel, thread, rec.lastDeliveredTs);
|
|
22516
23142
|
const blocks = [];
|
|
@@ -22582,11 +23208,8 @@ function routeRules(msg, rules, threadOwner) {
|
|
|
22582
23208
|
if (msg.thread) {
|
|
22583
23209
|
const owner = threadOwner(msg.channel, msg.thread);
|
|
22584
23210
|
if (owner) {
|
|
22585
|
-
const
|
|
22586
|
-
if (
|
|
22587
|
-
if (reachable.size === 1) return pickRule(scopeCandidates.find((x) => x.agentId === owner));
|
|
22588
|
-
return null;
|
|
22589
|
-
}
|
|
23211
|
+
const ownerRule = scopeCandidates.find((x) => x.agentId === owner);
|
|
23212
|
+
if (ownerRule) return pickRule(ownerRule);
|
|
22590
23213
|
}
|
|
22591
23214
|
}
|
|
22592
23215
|
const layer = kindCandidates.some((r) => r.source === "cp" && r.scope.channel === msg.channel && (r.scope.thread === void 0 || r.scope.thread === msg.thread)) ? kindCandidates.filter((r) => r.source === "cp") : kindCandidates;
|
|
@@ -80031,11 +80654,17 @@ var SystemClock = class {
|
|
|
80031
80654
|
const systemClock = new SystemClock();
|
|
80032
80655
|
//#endregion
|
|
80033
80656
|
//#region src/daemon.ts
|
|
80034
|
-
|
|
80035
|
-
|
|
80036
|
-
|
|
80037
|
-
|
|
80038
|
-
|
|
80657
|
+
/** Format an error for logs, surfacing a JSON-RPC/ACP RequestError's `code` and
|
|
80658
|
+
* `data` — for an agent-side `Internal error` the actionable detail (the adapter's
|
|
80659
|
+
* underlying exception) lives in `data`, which a bare `.stack` discards. */
|
|
80660
|
+
function formatErr(err) {
|
|
80661
|
+
const e = err;
|
|
80662
|
+
if (e && typeof e.code === "number") {
|
|
80663
|
+
const data = e.data === void 0 ? "" : ` data=${typeof e.data === "string" ? e.data : JSON.stringify(e.data)}`;
|
|
80664
|
+
return `${e.name ?? "Error"}: ${e.message ?? ""} (code=${e.code})${data}`;
|
|
80665
|
+
}
|
|
80666
|
+
return e?.stack ?? String(err);
|
|
80667
|
+
}
|
|
80039
80668
|
const MAX_QUEUED_PER_SESSION = 10;
|
|
80040
80669
|
var Daemon = class {
|
|
80041
80670
|
opts;
|
|
@@ -80111,7 +80740,7 @@ var Daemon = class {
|
|
|
80111
80740
|
agentById: (id) => this.agents.get(id)
|
|
80112
80741
|
});
|
|
80113
80742
|
this.scheduler = new Scheduler({
|
|
80114
|
-
onFire: (agentId, msg) => void this.dispatch(agentId, msg).catch((err) => this.log.error(`cron dispatch failed for agent "${agentId}": ${err
|
|
80743
|
+
onFire: (agentId, msg) => void this.dispatch(agentId, msg).catch((err) => this.log.error(`cron dispatch failed for agent "${agentId}": ${formatErr(err)}`)),
|
|
80115
80744
|
newTraceId: () => randomUUID()
|
|
80116
80745
|
});
|
|
80117
80746
|
const groups = consolidate(agents);
|
|
@@ -80161,7 +80790,10 @@ var Daemon = class {
|
|
|
80161
80790
|
return this.opts.agentName ? [selectAgent(this.agentsDir, this.opts.agentName)] : loadAgents(this.agentsDir);
|
|
80162
80791
|
}
|
|
80163
80792
|
async reconcile() {
|
|
80164
|
-
const
|
|
80793
|
+
const desired = this.loadAgentList();
|
|
80794
|
+
const { toStart, toStop, toRestart } = diffAgents(desired, this.agents);
|
|
80795
|
+
if (toStart.length || toStop.length || toRestart.length) this.log.info(`reconcile: ${desired.length} desired agent(s) from ${this.agentsDir}; start=[${toStart.map((a) => a.id).join(", ")}] stop=[${toStop.join(", ")}] restart=[${toRestart.map((a) => a.id).join(", ")}]`);
|
|
80796
|
+
else this.log.debug(`reconcile: no changes (${desired.length} desired agent(s))`);
|
|
80165
80797
|
for (const id of toStop) {
|
|
80166
80798
|
const host = this.hosts.get(id);
|
|
80167
80799
|
if (host) {
|
|
@@ -80171,6 +80803,15 @@ var Daemon = class {
|
|
|
80171
80803
|
this.hostStarts.delete(id);
|
|
80172
80804
|
this.agents.delete(id);
|
|
80173
80805
|
}
|
|
80806
|
+
for (const a of toRestart) {
|
|
80807
|
+
const host = this.hosts.get(a.id);
|
|
80808
|
+
if (host) {
|
|
80809
|
+
await host.stop();
|
|
80810
|
+
this.hosts.delete(a.id);
|
|
80811
|
+
}
|
|
80812
|
+
this.hostStarts.delete(a.id);
|
|
80813
|
+
this.agents.set(a.id, a);
|
|
80814
|
+
}
|
|
80174
80815
|
for (const a of toStart) this.agents.set(a.id, a);
|
|
80175
80816
|
}
|
|
80176
80817
|
ensureHost(agentId, cfg) {
|
|
@@ -80184,7 +80825,8 @@ var Daemon = class {
|
|
|
80184
80825
|
if (!runtime) throw new Error(`runtime "${agent.runtime}" not available: not installed on this host, or absent from config.runtimes / the ACP registry`);
|
|
80185
80826
|
host = new AcpHost(runtime, {
|
|
80186
80827
|
onUpdate,
|
|
80187
|
-
env: agentChildEnv(agent)
|
|
80828
|
+
env: agentChildEnv(agent),
|
|
80829
|
+
log: this.log
|
|
80188
80830
|
});
|
|
80189
80831
|
}
|
|
80190
80832
|
this.hosts.set(agentId, host);
|
|
@@ -80209,7 +80851,7 @@ var Daemon = class {
|
|
|
80209
80851
|
return;
|
|
80210
80852
|
}
|
|
80211
80853
|
this.log.info(`routing: ch=${msg.channel} → agent "${result.agentId}" (integration ${result.integrationId})`);
|
|
80212
|
-
this.dispatch(result.agentId, msg, result.integrationId).catch((err) => this.log.error(`dispatch failed for agent "${result.agentId}": ${err
|
|
80854
|
+
this.dispatch(result.agentId, msg, result.integrationId).catch((err) => this.log.error(`dispatch failed for agent "${result.agentId}": ${formatErr(err)}`));
|
|
80213
80855
|
}
|
|
80214
80856
|
queued = /* @__PURE__ */ new Map();
|
|
80215
80857
|
/**
|
|
@@ -80298,7 +80940,7 @@ var Daemon = class {
|
|
|
80298
80940
|
const replyConn = this.replyConnFor(agentId, integrationId);
|
|
80299
80941
|
const wasRunning = this.hostStarts.has(agentId);
|
|
80300
80942
|
const statusThread = msg.thread ?? msg.msgId;
|
|
80301
|
-
replyConn?.setStatus(msg.channel, statusThread, wasRunning ? "is thinking…" : "is starting up…"
|
|
80943
|
+
replyConn?.setStatus(msg.channel, statusThread, wasRunning ? "is thinking…" : "is starting up…");
|
|
80302
80944
|
const { sessionId, blocks } = await this.sessions.handle(agentId, msg);
|
|
80303
80945
|
this.pending.set(sessionId, {
|
|
80304
80946
|
conv,
|
|
@@ -80308,7 +80950,7 @@ var Daemon = class {
|
|
|
80308
80950
|
});
|
|
80309
80951
|
try {
|
|
80310
80952
|
const host = await this.ensureHostAsync(agentId);
|
|
80311
|
-
if (!wasRunning) replyConn?.setStatus(msg.channel, statusThread, "is thinking…"
|
|
80953
|
+
if (!wasRunning) replyConn?.setStatus(msg.channel, statusThread, "is thinking…");
|
|
80312
80954
|
await host.prompt(sessionId, blocks);
|
|
80313
80955
|
for (const action of conv.onFinal(`local://session/${sessionId}`)) await this.applyAction(action, replyConn, msg.channel, statusThread);
|
|
80314
80956
|
} finally {
|
|
@@ -80316,10 +80958,10 @@ var Daemon = class {
|
|
|
80316
80958
|
}
|
|
80317
80959
|
this.flushQueued(agentId, sessionId, integrationId);
|
|
80318
80960
|
}
|
|
80319
|
-
/** Route a converger action: set-status → setStatus (
|
|
80961
|
+
/** Route a converger action: set-status → setStatus (status text only; '' clears); else postMessage. */
|
|
80320
80962
|
async applyAction(action, conn, channel, thread) {
|
|
80321
80963
|
if (action.kind === "set-status") {
|
|
80322
|
-
if (conn && thread) await conn.setStatus(channel, thread, action.text
|
|
80964
|
+
if (conn && thread) await conn.setStatus(channel, thread, action.text);
|
|
80323
80965
|
return;
|
|
80324
80966
|
}
|
|
80325
80967
|
await conn?.postMessage(channel, action.text, thread);
|