@agentconnect.md/daemon 1.0.0-rc.24 → 1.0.0-rc.25
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 +169 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7567,6 +7567,9 @@ const AgentSchema = object({
|
|
|
7567
7567
|
"paused"
|
|
7568
7568
|
]).default("active"),
|
|
7569
7569
|
runtime: string(),
|
|
7570
|
+
description: string().optional(),
|
|
7571
|
+
reasoningEffort: string().optional(),
|
|
7572
|
+
executionMode: string().optional(),
|
|
7570
7573
|
runtimeOverrides: object({
|
|
7571
7574
|
model: string().optional(),
|
|
7572
7575
|
env: array(object({
|
|
@@ -22985,6 +22988,44 @@ function watch$1(paths, options = {}) {
|
|
|
22985
22988
|
return watcher;
|
|
22986
22989
|
}
|
|
22987
22990
|
//#endregion
|
|
22991
|
+
//#region src/agents/cp-overlay.ts
|
|
22992
|
+
/** Apply a CP `AgentSpec` onto a local agent, returning a new overlaid agent. */
|
|
22993
|
+
function overlayCpSpec(base, spec) {
|
|
22994
|
+
const envMap = new Map((base.runtimeOverrides?.env ?? []).map((e) => [e.name, e.value]));
|
|
22995
|
+
for (const [name, value] of Object.entries(spec.env ?? {})) envMap.set(name, value);
|
|
22996
|
+
const env = [...envMap].map(([name, value]) => ({
|
|
22997
|
+
name,
|
|
22998
|
+
value
|
|
22999
|
+
}));
|
|
23000
|
+
return {
|
|
23001
|
+
...base,
|
|
23002
|
+
name: spec.name,
|
|
23003
|
+
...spec.description !== void 0 ? { description: spec.description } : {},
|
|
23004
|
+
...spec.reasoningEffort !== void 0 ? { reasoningEffort: spec.reasoningEffort } : {},
|
|
23005
|
+
...spec.executionMode !== void 0 ? { executionMode: spec.executionMode } : {},
|
|
23006
|
+
runtimeOverrides: {
|
|
23007
|
+
...base.runtimeOverrides,
|
|
23008
|
+
...spec.model !== void 0 ? { model: spec.model } : {},
|
|
23009
|
+
env
|
|
23010
|
+
}
|
|
23011
|
+
};
|
|
23012
|
+
}
|
|
23013
|
+
/**
|
|
23014
|
+
* Runtime config the daemon can only surface to an ACP child as environment
|
|
23015
|
+
* variables: ACP `session/new`/`initialize` carry no model or system-prompt
|
|
23016
|
+
* field (SDK v1), so model / reasoning / prompt are exposed under `AGENTCONNECT_*`
|
|
23017
|
+
* for runtimes that read them. (`runtimeOverrides.env` is applied separately by
|
|
23018
|
+
* `agentChildEnv`.) Only emits a key when the value is set.
|
|
23019
|
+
*/
|
|
23020
|
+
function cpRuntimeEnv(agent) {
|
|
23021
|
+
const out = {};
|
|
23022
|
+
if (agent.runtimeOverrides?.model) out.AGENTCONNECT_MODEL = agent.runtimeOverrides.model;
|
|
23023
|
+
if (agent.reasoningEffort) out.AGENTCONNECT_REASONING_EFFORT = agent.reasoningEffort;
|
|
23024
|
+
if (agent.executionMode) out.AGENTCONNECT_EXECUTION_MODE = agent.executionMode;
|
|
23025
|
+
if (agent.description) out.AGENTCONNECT_SYSTEM_PROMPT = agent.description;
|
|
23026
|
+
return out;
|
|
23027
|
+
}
|
|
23028
|
+
//#endregion
|
|
22988
23029
|
//#region src/reconciler/reconciler.ts
|
|
22989
23030
|
/** Stable identity-free signature of an agent's effective config. Excludes the
|
|
22990
23031
|
* loader-only `dir`/`env` fields (present on `LoadedAgent`) so the comparison is
|
|
@@ -23042,6 +23083,10 @@ var LocalStore = class {
|
|
|
23042
23083
|
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
23043
23084
|
routingEpoch INTEGER, assignments TEXT, globalRules TEXT
|
|
23044
23085
|
);
|
|
23086
|
+
CREATE TABLE IF NOT EXISTS cp_agents (
|
|
23087
|
+
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
23088
|
+
specs TEXT
|
|
23089
|
+
);
|
|
23045
23090
|
`);
|
|
23046
23091
|
}
|
|
23047
23092
|
getSession(key) {
|
|
@@ -23075,6 +23120,13 @@ var LocalStore = class {
|
|
|
23075
23120
|
globalRules
|
|
23076
23121
|
});
|
|
23077
23122
|
}
|
|
23123
|
+
getCpAgents() {
|
|
23124
|
+
return this.db.prepare("SELECT specs FROM cp_agents WHERE id = 1").get();
|
|
23125
|
+
}
|
|
23126
|
+
setCpAgents(specs) {
|
|
23127
|
+
this.db.prepare(`INSERT INTO cp_agents (id, specs) VALUES (1, @specs)
|
|
23128
|
+
ON CONFLICT(id) DO UPDATE SET specs=excluded.specs`).run({ specs });
|
|
23129
|
+
}
|
|
23078
23130
|
close() {
|
|
23079
23131
|
this.db.close();
|
|
23080
23132
|
}
|
|
@@ -80533,6 +80585,14 @@ var CpClient = class {
|
|
|
80533
80585
|
case "route/update":
|
|
80534
80586
|
this.deps.configApply.applyRouteUpdate(frame.payload);
|
|
80535
80587
|
return;
|
|
80588
|
+
case "agent/upsert": {
|
|
80589
|
+
const u = frame.payload;
|
|
80590
|
+
this.deps.configApply.applyAgentUpsert(u.agentId, u.spec);
|
|
80591
|
+
return;
|
|
80592
|
+
}
|
|
80593
|
+
case "agent/remove":
|
|
80594
|
+
this.deps.configApply.applyAgentRemove(frame.payload.agentId);
|
|
80595
|
+
return;
|
|
80536
80596
|
case "agent/launch":
|
|
80537
80597
|
case "agent/stop":
|
|
80538
80598
|
case "agent/prompt":
|
|
@@ -80586,6 +80646,46 @@ var CpCronRegistry = class {
|
|
|
80586
80646
|
}
|
|
80587
80647
|
};
|
|
80588
80648
|
//#endregion
|
|
80649
|
+
//#region src/cp/cp-agent-registry.ts
|
|
80650
|
+
var CpAgentRegistry = class {
|
|
80651
|
+
io;
|
|
80652
|
+
onChange;
|
|
80653
|
+
specs = /* @__PURE__ */ new Map();
|
|
80654
|
+
constructor(io, onChange) {
|
|
80655
|
+
this.io = io;
|
|
80656
|
+
this.onChange = onChange;
|
|
80657
|
+
const s = io.load();
|
|
80658
|
+
if (s) this.specs = new Map(Object.entries(s));
|
|
80659
|
+
}
|
|
80660
|
+
/** Add or replace one agent's spec (agent/upsert EVT). */
|
|
80661
|
+
upsert(agentId, spec) {
|
|
80662
|
+
this.specs.set(agentId, spec);
|
|
80663
|
+
this.changed();
|
|
80664
|
+
}
|
|
80665
|
+
/** Drop one agent's spec (agent/remove EVT). No-op if absent. */
|
|
80666
|
+
remove(agentId) {
|
|
80667
|
+
if (this.specs.delete(agentId)) this.changed();
|
|
80668
|
+
}
|
|
80669
|
+
/** Make the live set exactly `roster` (register/ok reconcile snapshot). */
|
|
80670
|
+
converge(roster) {
|
|
80671
|
+
const next = /* @__PURE__ */ new Map();
|
|
80672
|
+
for (const { agentId, ...spec } of roster) next.set(agentId, spec);
|
|
80673
|
+
this.specs = next;
|
|
80674
|
+
this.changed();
|
|
80675
|
+
}
|
|
80676
|
+
get(agentId) {
|
|
80677
|
+
return this.specs.get(agentId);
|
|
80678
|
+
}
|
|
80679
|
+
/** agentIds the CP currently wants present. */
|
|
80680
|
+
ids() {
|
|
80681
|
+
return [...this.specs.keys()];
|
|
80682
|
+
}
|
|
80683
|
+
changed() {
|
|
80684
|
+
this.io.save(Object.fromEntries(this.specs));
|
|
80685
|
+
this.onChange();
|
|
80686
|
+
}
|
|
80687
|
+
};
|
|
80688
|
+
//#endregion
|
|
80589
80689
|
//#region src/cp/config-apply.ts
|
|
80590
80690
|
const LOG_LEVELS = /* @__PURE__ */ new Set([
|
|
80591
80691
|
"trace",
|
|
@@ -80670,6 +80770,7 @@ var Daemon = class {
|
|
|
80670
80770
|
opts;
|
|
80671
80771
|
store;
|
|
80672
80772
|
agents = /* @__PURE__ */ new Map();
|
|
80773
|
+
fileAgents = /* @__PURE__ */ new Map();
|
|
80673
80774
|
hosts = /* @__PURE__ */ new Map();
|
|
80674
80775
|
connections = [];
|
|
80675
80776
|
scheduler;
|
|
@@ -80686,6 +80787,7 @@ var Daemon = class {
|
|
|
80686
80787
|
runtimeNames = {};
|
|
80687
80788
|
cpClient;
|
|
80688
80789
|
cpCrons;
|
|
80790
|
+
cpAgents;
|
|
80689
80791
|
botUserIds = {};
|
|
80690
80792
|
cpRouting;
|
|
80691
80793
|
constructor(opts = {}) {
|
|
@@ -80710,7 +80812,7 @@ var Daemon = class {
|
|
|
80710
80812
|
this.log.info(`control plane: ${cfg.controlPlane?.enabled ? `enabled (${cfg.controlPlane.url ?? "no url"})` : "disabled — running local"}`);
|
|
80711
80813
|
this.agentsDir = cfg.agentsDir;
|
|
80712
80814
|
const agents = this.loadAgentList();
|
|
80713
|
-
|
|
80815
|
+
this.fileAgents = new Map(agents.map((a) => [a.id, a]));
|
|
80714
80816
|
this.log.info(`loaded ${agents.length} agent(s) from ${this.agentsDir}${agents.length ? `: ${agents.map((a) => a.id).join(", ")}` : ""}`);
|
|
80715
80817
|
this.root = root;
|
|
80716
80818
|
const resolvedRuntimes = await resolveRuntimes(cfg, root, {
|
|
@@ -80734,6 +80836,14 @@ var Daemon = class {
|
|
|
80734
80836
|
},
|
|
80735
80837
|
save: (s) => this.store.setCpRouting(s.routingEpoch, JSON.stringify(s.assignments), JSON.stringify(s.globalRules))
|
|
80736
80838
|
});
|
|
80839
|
+
this.cpAgents = new CpAgentRegistry({
|
|
80840
|
+
load: () => {
|
|
80841
|
+
const row = this.store.getCpAgents();
|
|
80842
|
+
return row ? JSON.parse(row.specs) : void 0;
|
|
80843
|
+
},
|
|
80844
|
+
save: (s) => this.store.setCpAgents(JSON.stringify(s))
|
|
80845
|
+
}, () => void this.reconcile().catch((err) => this.log.error(`cp: agent reconcile failed: ${err.stack ?? err}`)));
|
|
80846
|
+
for (const a of this.effectiveAgents()) this.agents.set(a.id, a);
|
|
80737
80847
|
this.sessions = new SessionManager({
|
|
80738
80848
|
store: this.store,
|
|
80739
80849
|
hostFor: (agentId) => this.ensureHostAsync(agentId),
|
|
@@ -80789,8 +80899,51 @@ var Daemon = class {
|
|
|
80789
80899
|
loadAgentList() {
|
|
80790
80900
|
return this.opts.agentName ? [selectAgent(this.agentsDir, this.opts.agentName)] : loadAgents(this.agentsDir);
|
|
80791
80901
|
}
|
|
80902
|
+
/**
|
|
80903
|
+
* Effective agent set = on-disk `agent.json` base with the CP spec overlaid
|
|
80904
|
+
* (joined by id; CP owns name/description/model/reasoning/execution/env, the
|
|
80905
|
+
* file owns runtime/workspace). CP-only ids with no local base are skipped —
|
|
80906
|
+
* not runnable — and surfaced via cpDegradedScopes().
|
|
80907
|
+
*/
|
|
80908
|
+
effectiveAgents() {
|
|
80909
|
+
return [...this.fileAgents.values()].map((base) => {
|
|
80910
|
+
const spec = this.cpAgents?.get(base.id);
|
|
80911
|
+
return spec ? overlayCpSpec(base, spec) : base;
|
|
80912
|
+
});
|
|
80913
|
+
}
|
|
80914
|
+
/**
|
|
80915
|
+
* Converge the running set to the desired effective agents. Driven by both the
|
|
80916
|
+
* `agents/**` file-watch AND CP convergence (agent/upsert, agent/remove, the
|
|
80917
|
+
* register/ok roster, via the registry's onChange). `diffAgents` detects a
|
|
80918
|
+
* changed effective config (runtime/workspace/model/prompt/env/…) and restarts
|
|
80919
|
+
* the host lazily so the next session picks up fresh config.
|
|
80920
|
+
*
|
|
80921
|
+
* Single-flight: overlapping triggers (e.g. a burst of CP frames, or a file
|
|
80922
|
+
* event landing mid-reconcile) coalesce into one trailing re-run so we never
|
|
80923
|
+
* run two passes concurrently and double-evict a host.
|
|
80924
|
+
*/
|
|
80925
|
+
reconcileRun;
|
|
80926
|
+
reconcilePending = false;
|
|
80792
80927
|
async reconcile() {
|
|
80793
|
-
|
|
80928
|
+
if (this.reconcileRun) {
|
|
80929
|
+
this.reconcilePending = true;
|
|
80930
|
+
return this.reconcileRun;
|
|
80931
|
+
}
|
|
80932
|
+
this.reconcileRun = this.runReconcile();
|
|
80933
|
+
try {
|
|
80934
|
+
await this.reconcileRun;
|
|
80935
|
+
} finally {
|
|
80936
|
+
this.reconcileRun = void 0;
|
|
80937
|
+
}
|
|
80938
|
+
if (this.reconcilePending) {
|
|
80939
|
+
this.reconcilePending = false;
|
|
80940
|
+
await this.reconcile();
|
|
80941
|
+
}
|
|
80942
|
+
}
|
|
80943
|
+
async runReconcile() {
|
|
80944
|
+
const files = this.loadAgentList();
|
|
80945
|
+
this.fileAgents = new Map(files.map((a) => [a.id, a]));
|
|
80946
|
+
const desired = this.effectiveAgents();
|
|
80794
80947
|
const { toStart, toStop, toRestart } = diffAgents(desired, this.agents);
|
|
80795
80948
|
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
80949
|
else this.log.debug(`reconcile: no changes (${desired.length} desired agent(s))`);
|
|
@@ -80825,7 +80978,10 @@ var Daemon = class {
|
|
|
80825
80978
|
if (!runtime) throw new Error(`runtime "${agent.runtime}" not available: not installed on this host, or absent from config.runtimes / the ACP registry`);
|
|
80826
80979
|
host = new AcpHost(runtime, {
|
|
80827
80980
|
onUpdate,
|
|
80828
|
-
env:
|
|
80981
|
+
env: {
|
|
80982
|
+
...agentChildEnv(agent),
|
|
80983
|
+
...cpRuntimeEnv(agent)
|
|
80984
|
+
},
|
|
80829
80985
|
log: this.log
|
|
80830
80986
|
});
|
|
80831
80987
|
}
|
|
@@ -80929,10 +81085,15 @@ var Daemon = class {
|
|
|
80929
81085
|
resolveCpAgent(agentId) {
|
|
80930
81086
|
return resolveAgentIntegration(this.agents.get(agentId), this.botUserIds);
|
|
80931
81087
|
}
|
|
80932
|
-
/**
|
|
81088
|
+
/**
|
|
81089
|
+
* agentIds the daemon can't fully serve: CP routing rules with no servable
|
|
81090
|
+
* Slack integration, plus CP agent specs with no on-disk base (no runtime /
|
|
81091
|
+
* workspace to materialize — the spec alone can't be run).
|
|
81092
|
+
*/
|
|
80933
81093
|
cpDegradedScopes() {
|
|
80934
81094
|
const out = /* @__PURE__ */ new Set();
|
|
80935
81095
|
for (const cpRule of this.cpRouting?.effectiveRules() ?? []) if (!this.resolveCpAgent(cpRule.agentId)) out.add(cpRule.agentId);
|
|
81096
|
+
for (const id of this.cpAgents?.ids() ?? []) if (!this.fileAgents.has(id)) out.add(id);
|
|
80936
81097
|
return [...out];
|
|
80937
81098
|
}
|
|
80938
81099
|
async dispatch(agentId, msg, integrationId) {
|
|
@@ -80996,6 +81157,7 @@ var Daemon = class {
|
|
|
80996
81157
|
},
|
|
80997
81158
|
applyReconcileSnapshot: (snap) => {
|
|
80998
81159
|
this.cpCrons?.converge(snap.crons);
|
|
81160
|
+
this.cpAgents?.converge(snap.agents);
|
|
80999
81161
|
this.cpRouting?.converge({
|
|
81000
81162
|
routingEpoch: snap.routingEpoch,
|
|
81001
81163
|
assignments: snap.assignments,
|
|
@@ -81003,7 +81165,10 @@ var Daemon = class {
|
|
|
81003
81165
|
});
|
|
81004
81166
|
if (snap.leases.length) this.log.debug(`cp: ${snap.leases.length} lease(s) noted (secrets handled later)`);
|
|
81005
81167
|
if (snap.assignments.length) this.log.debug(`cp: converged ${snap.assignments.length} assignment(s)`);
|
|
81168
|
+
if (snap.agents.length) this.log.debug(`cp: converged ${snap.agents.length} agent spec(s)`);
|
|
81006
81169
|
},
|
|
81170
|
+
applyAgentUpsert: (agentId, spec) => this.cpAgents?.upsert(agentId, spec),
|
|
81171
|
+
applyAgentRemove: (agentId) => this.cpAgents?.remove(agentId),
|
|
81007
81172
|
upsertCron: (cron) => this.cpCrons.upsert(cron),
|
|
81008
81173
|
removeCron: (cronId) => this.cpCrons.remove(cronId),
|
|
81009
81174
|
applyRouteAssign: (a) => this.cpRouting?.upsertAssign(a),
|