@botcord/daemon 0.2.5 → 0.2.8
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/agent-discovery.d.ts +4 -0
- package/dist/agent-discovery.js +8 -0
- package/dist/agent-workspace.d.ts +62 -0
- package/dist/agent-workspace.js +140 -8
- package/dist/config.d.ts +64 -1
- package/dist/config.js +73 -1
- package/dist/daemon-config-map.d.ts +27 -9
- package/dist/daemon-config-map.js +105 -8
- package/dist/daemon.d.ts +2 -0
- package/dist/daemon.js +76 -6
- package/dist/doctor.d.ts +27 -1
- package/dist/doctor.js +22 -1
- package/dist/gateway/cli-resolver.d.ts +34 -0
- package/dist/gateway/cli-resolver.js +74 -0
- package/dist/gateway/dispatcher.d.ts +31 -1
- package/dist/gateway/dispatcher.js +337 -29
- package/dist/gateway/gateway.d.ts +29 -1
- package/dist/gateway/gateway.js +10 -0
- package/dist/gateway/index.d.ts +2 -0
- package/dist/gateway/index.js +2 -0
- package/dist/gateway/policy-resolver.d.ts +57 -0
- package/dist/gateway/policy-resolver.js +123 -0
- package/dist/gateway/runtimes/acp-stream.d.ts +99 -0
- package/dist/gateway/runtimes/acp-stream.js +394 -0
- package/dist/gateway/runtimes/codex.js +7 -0
- package/dist/gateway/runtimes/hermes-agent.d.ts +83 -0
- package/dist/gateway/runtimes/hermes-agent.js +180 -0
- package/dist/gateway/runtimes/ndjson-stream.d.ts +7 -2
- package/dist/gateway/runtimes/ndjson-stream.js +16 -3
- package/dist/gateway/runtimes/openclaw-acp.d.ts +44 -0
- package/dist/gateway/runtimes/openclaw-acp.js +500 -0
- package/dist/gateway/runtimes/registry.d.ts +4 -0
- package/dist/gateway/runtimes/registry.js +22 -0
- package/dist/gateway/transcript-paths.d.ts +30 -0
- package/dist/gateway/transcript-paths.js +114 -0
- package/dist/gateway/transcript.d.ts +123 -0
- package/dist/gateway/transcript.js +147 -0
- package/dist/gateway/types.d.ts +31 -0
- package/dist/index.js +309 -27
- package/dist/mention-scan.d.ts +22 -0
- package/dist/mention-scan.js +35 -0
- package/dist/openclaw-discovery.d.ts +28 -0
- package/dist/openclaw-discovery.js +228 -0
- package/dist/provision.d.ts +113 -1
- package/dist/provision.js +564 -12
- package/dist/system-context.d.ts +5 -4
- package/dist/system-context.js +35 -5
- package/dist/url-utils.d.ts +9 -0
- package/dist/url-utils.js +18 -0
- package/package.json +3 -2
- package/src/__tests__/agent-workspace.test.ts +93 -0
- package/src/__tests__/daemon-config-map.test.ts +79 -0
- package/src/__tests__/openclaw-acp.test.ts +234 -0
- package/src/__tests__/openclaw-discovery.test.ts +150 -0
- package/src/__tests__/policy-resolver.test.ts +124 -0
- package/src/__tests__/policy-updated-handler.test.ts +144 -0
- package/src/__tests__/provision.test.ts +265 -0
- package/src/__tests__/system-context.test.ts +52 -0
- package/src/__tests__/url-utils.test.ts +37 -0
- package/src/agent-discovery.ts +8 -0
- package/src/agent-workspace.ts +173 -7
- package/src/config.ts +168 -4
- package/src/daemon-config-map.ts +154 -9
- package/src/daemon.ts +96 -6
- package/src/doctor.ts +49 -2
- package/src/gateway/__tests__/dispatcher.test.ts +65 -0
- package/src/gateway/__tests__/hermes-agent-adapter.test.ts +302 -0
- package/src/gateway/__tests__/transcript.test.ts +496 -0
- package/src/gateway/cli-resolver.ts +92 -0
- package/src/gateway/dispatcher.ts +394 -26
- package/src/gateway/gateway.ts +46 -0
- package/src/gateway/index.ts +25 -0
- package/src/gateway/policy-resolver.ts +171 -0
- package/src/gateway/runtimes/acp-stream.ts +535 -0
- package/src/gateway/runtimes/codex.ts +7 -0
- package/src/gateway/runtimes/hermes-agent.ts +206 -0
- package/src/gateway/runtimes/ndjson-stream.ts +16 -3
- package/src/gateway/runtimes/openclaw-acp.ts +606 -0
- package/src/gateway/runtimes/registry.ts +24 -0
- package/src/gateway/transcript-paths.ts +145 -0
- package/src/gateway/transcript.ts +300 -0
- package/src/gateway/types.ts +32 -0
- package/src/index.ts +321 -30
- package/src/mention-scan.ts +38 -0
- package/src/openclaw-discovery.ts +262 -0
- package/src/provision.ts +682 -14
- package/src/system-context.ts +41 -9
- package/src/url-utils.ts +17 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { log as daemonLog } from "./log.js";
|
|
5
|
+
import { probeOpenclawAgents } from "./provision.js";
|
|
6
|
+
const DEFAULT_SEARCH_PATHS = ["~/.openclaw/", "/etc/openclaw/"];
|
|
7
|
+
const DEFAULT_PORTS = [18789];
|
|
8
|
+
export async function discoverLocalOpenclawGateways(opts = {}) {
|
|
9
|
+
const found = [];
|
|
10
|
+
for (const root of opts.searchPaths ?? DEFAULT_SEARCH_PATHS) {
|
|
11
|
+
found.push(...discoverFromConfigDir(root));
|
|
12
|
+
}
|
|
13
|
+
const env = opts.env ?? process.env;
|
|
14
|
+
const envUrl = env.OPENCLAW_ACP_URL;
|
|
15
|
+
if (envUrl) {
|
|
16
|
+
const item = {
|
|
17
|
+
name: nameFromUrl(envUrl),
|
|
18
|
+
url: envUrl,
|
|
19
|
+
source: "env",
|
|
20
|
+
};
|
|
21
|
+
if (env.OPENCLAW_ACP_TOKEN)
|
|
22
|
+
item.token = env.OPENCLAW_ACP_TOKEN;
|
|
23
|
+
else if (env.OPENCLAW_ACP_TOKEN_FILE)
|
|
24
|
+
item.tokenFile = env.OPENCLAW_ACP_TOKEN_FILE;
|
|
25
|
+
found.push(item);
|
|
26
|
+
}
|
|
27
|
+
const ports = opts.defaultPorts ?? DEFAULT_PORTS;
|
|
28
|
+
if (ports.length > 0) {
|
|
29
|
+
await Promise.all(ports.map(async (port) => {
|
|
30
|
+
const url = `ws://127.0.0.1:${port}`;
|
|
31
|
+
try {
|
|
32
|
+
const res = await probeOpenclawAgents({ url }, { probe: opts.probe, timeoutMs: opts.timeoutMs });
|
|
33
|
+
if (res.ok) {
|
|
34
|
+
found.push({ name: nameFromUrl(url), url, source: "default-port" });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
daemonLog.debug("openclaw discovery default-port probe failed", {
|
|
39
|
+
url,
|
|
40
|
+
error: err instanceof Error ? err.message : String(err),
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}));
|
|
44
|
+
}
|
|
45
|
+
return dedupeDiscovered(found);
|
|
46
|
+
}
|
|
47
|
+
export function mergeOpenclawGateways(cfg, found) {
|
|
48
|
+
const existing = cfg.openclawGateways ?? [];
|
|
49
|
+
const existingUrls = new Set(existing.map((g) => normalizeUrlKey(g.url)));
|
|
50
|
+
const existingNames = new Set(existing.map((g) => g.name));
|
|
51
|
+
const added = [];
|
|
52
|
+
for (const item of found) {
|
|
53
|
+
const key = normalizeUrlKey(item.url);
|
|
54
|
+
if (existingUrls.has(key))
|
|
55
|
+
continue;
|
|
56
|
+
const profile = {
|
|
57
|
+
name: uniqueName(item.name, existingNames),
|
|
58
|
+
url: item.url,
|
|
59
|
+
};
|
|
60
|
+
if (item.token)
|
|
61
|
+
profile.token = item.token;
|
|
62
|
+
else if (item.tokenFile)
|
|
63
|
+
profile.tokenFile = item.tokenFile;
|
|
64
|
+
existingUrls.add(key);
|
|
65
|
+
existingNames.add(profile.name);
|
|
66
|
+
added.push(profile);
|
|
67
|
+
}
|
|
68
|
+
if (added.length === 0)
|
|
69
|
+
return { cfg, changed: false, added };
|
|
70
|
+
return {
|
|
71
|
+
cfg: { ...cfg, openclawGateways: [...existing, ...added] },
|
|
72
|
+
changed: true,
|
|
73
|
+
added,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function discoverFromConfigDir(root) {
|
|
77
|
+
const dir = expandHome(root);
|
|
78
|
+
let names;
|
|
79
|
+
try {
|
|
80
|
+
names = readdirSync(dir);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return [];
|
|
84
|
+
}
|
|
85
|
+
const out = [];
|
|
86
|
+
for (const name of names.sort()) {
|
|
87
|
+
if (!name.endsWith(".json") && !name.endsWith(".toml"))
|
|
88
|
+
continue;
|
|
89
|
+
const file = path.join(dir, name);
|
|
90
|
+
try {
|
|
91
|
+
const st = statSync(file);
|
|
92
|
+
if (!st.isFile())
|
|
93
|
+
continue;
|
|
94
|
+
const raw = readFileSync(file, "utf8");
|
|
95
|
+
const parsed = name.endsWith(".json") ? parseJsonConfig(raw) : parseTomlConfig(raw);
|
|
96
|
+
if (!parsed?.url)
|
|
97
|
+
continue;
|
|
98
|
+
const item = {
|
|
99
|
+
name: nameFromUrl(parsed.url),
|
|
100
|
+
url: parsed.url,
|
|
101
|
+
source: "config-file",
|
|
102
|
+
};
|
|
103
|
+
if (parsed.token)
|
|
104
|
+
item.token = parsed.token;
|
|
105
|
+
else if (parsed.tokenFile)
|
|
106
|
+
item.tokenFile = parsed.tokenFile;
|
|
107
|
+
out.push(item);
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
daemonLog.debug("openclaw discovery config skipped", {
|
|
111
|
+
file,
|
|
112
|
+
error: err instanceof Error ? err.message : String(err),
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return out;
|
|
117
|
+
}
|
|
118
|
+
function parseJsonConfig(raw) {
|
|
119
|
+
const obj = JSON.parse(raw);
|
|
120
|
+
const acp = obj?.acp ?? obj?.gateway?.acp ?? obj?.gateway ?? obj;
|
|
121
|
+
return pickConfigValues(acp);
|
|
122
|
+
}
|
|
123
|
+
function parseTomlConfig(raw) {
|
|
124
|
+
let inAcp = false;
|
|
125
|
+
const values = {};
|
|
126
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
127
|
+
const trimmed = line.replace(/#.*/, "").trim();
|
|
128
|
+
if (!trimmed)
|
|
129
|
+
continue;
|
|
130
|
+
const section = trimmed.match(/^\[([^\]]+)\]$/);
|
|
131
|
+
if (section) {
|
|
132
|
+
inAcp = section[1] === "acp" || section[1].endsWith(".acp");
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (!inAcp)
|
|
136
|
+
continue;
|
|
137
|
+
const m = trimmed.match(/^([A-Za-z0-9_-]+)\s*=\s*"(.*)"\s*$/);
|
|
138
|
+
if (m)
|
|
139
|
+
values[m[1]] = m[2];
|
|
140
|
+
}
|
|
141
|
+
return pickConfigValues(values);
|
|
142
|
+
}
|
|
143
|
+
function pickConfigValues(obj) {
|
|
144
|
+
if (!obj || typeof obj !== "object")
|
|
145
|
+
return null;
|
|
146
|
+
const url = pickString(obj, ["url", "wsUrl", "ws_url", "endpoint"]);
|
|
147
|
+
if (!url)
|
|
148
|
+
return null;
|
|
149
|
+
const token = pickString(obj, ["token", "bearerToken", "bearer_token"]);
|
|
150
|
+
const tokenFile = pickString(obj, ["tokenFile", "token_file"]);
|
|
151
|
+
return { url, token, tokenFile };
|
|
152
|
+
}
|
|
153
|
+
function pickString(obj, keys) {
|
|
154
|
+
for (const key of keys) {
|
|
155
|
+
const value = obj[key];
|
|
156
|
+
if (typeof value === "string" && value.trim())
|
|
157
|
+
return value.trim();
|
|
158
|
+
}
|
|
159
|
+
return undefined;
|
|
160
|
+
}
|
|
161
|
+
function dedupeDiscovered(items) {
|
|
162
|
+
const priority = {
|
|
163
|
+
"config-file": 3,
|
|
164
|
+
env: 2,
|
|
165
|
+
"default-port": 1,
|
|
166
|
+
};
|
|
167
|
+
const byUrl = new Map();
|
|
168
|
+
for (const item of items) {
|
|
169
|
+
const key = normalizeUrlKey(item.url);
|
|
170
|
+
const prev = byUrl.get(key);
|
|
171
|
+
if (!prev || priority[item.source] > priority[prev.source] || hasMoreAuth(item, prev)) {
|
|
172
|
+
byUrl.set(key, item);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return [...byUrl.values()];
|
|
176
|
+
}
|
|
177
|
+
function hasMoreAuth(a, b) {
|
|
178
|
+
const score = (x) => (x.token ? 2 : x.tokenFile ? 1 : 0);
|
|
179
|
+
return score(a) > score(b);
|
|
180
|
+
}
|
|
181
|
+
function nameFromUrl(raw) {
|
|
182
|
+
try {
|
|
183
|
+
const u = new URL(raw);
|
|
184
|
+
const base = `${u.hostname}-${u.port || (u.protocol === "wss:" ? "443" : "80")}`;
|
|
185
|
+
return `openclaw-${base.replace(/[^A-Za-z0-9_-]+/g, "-")}`;
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
return "openclaw-local";
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
function uniqueName(base, existing) {
|
|
192
|
+
let candidate = base;
|
|
193
|
+
let i = 2;
|
|
194
|
+
while (existing.has(candidate)) {
|
|
195
|
+
candidate = `${base}-${i}`;
|
|
196
|
+
i += 1;
|
|
197
|
+
}
|
|
198
|
+
return candidate;
|
|
199
|
+
}
|
|
200
|
+
function normalizeUrlKey(raw) {
|
|
201
|
+
try {
|
|
202
|
+
const u = new URL(raw);
|
|
203
|
+
u.hash = "";
|
|
204
|
+
return u.toString();
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
return raw.trim();
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
function expandHome(p) {
|
|
211
|
+
if (p === "~")
|
|
212
|
+
return homedir();
|
|
213
|
+
if (p.startsWith("~/"))
|
|
214
|
+
return path.join(homedir(), p.slice(2));
|
|
215
|
+
return p;
|
|
216
|
+
}
|
|
217
|
+
export function defaultOpenclawDiscoverySearchPaths() {
|
|
218
|
+
return DEFAULT_SEARCH_PATHS.slice();
|
|
219
|
+
}
|
|
220
|
+
export function defaultOpenclawDiscoveryPorts() {
|
|
221
|
+
return DEFAULT_PORTS.slice();
|
|
222
|
+
}
|
|
223
|
+
export function openclawDiscoveryConfigEnabled(cfg) {
|
|
224
|
+
return cfg.openclawDiscovery?.enabled !== false;
|
|
225
|
+
}
|
|
226
|
+
export function openclawAutoProvisionEnabled(cfg) {
|
|
227
|
+
return cfg.openclawDiscovery?.autoProvision !== false;
|
|
228
|
+
}
|
package/dist/provision.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { BotCordClient, type ControlAck, type ControlFrame, type ListRuntimesResult } from "@botcord/protocol-core";
|
|
1
|
+
import { BotCordClient, type AgentIdentitySnapshot, type ControlAck, type ControlFrame, type ListRuntimesResult } from "@botcord/protocol-core";
|
|
2
2
|
import type { Gateway } from "./gateway/index.js";
|
|
3
|
+
import type { PolicyResolverLike } from "./gateway/policy-resolver.js";
|
|
3
4
|
import { type DaemonConfig } from "./config.js";
|
|
4
5
|
/** Options accepted by {@link createProvisioner}. */
|
|
5
6
|
export interface ProvisionerOptions {
|
|
@@ -10,6 +11,14 @@ export interface ProvisionerOptions {
|
|
|
10
11
|
* run without a real Hub.
|
|
11
12
|
*/
|
|
12
13
|
register?: typeof BotCordClient.register;
|
|
14
|
+
/**
|
|
15
|
+
* Optional policy-resolver handle (PR3). When present, the
|
|
16
|
+
* `policy_updated` control frame routes through it: cache is invalidated
|
|
17
|
+
* for the (agent, room?) pair, and any embedded `policy` payload is
|
|
18
|
+
* applied directly so the next inbound sees the fresh policy without an
|
|
19
|
+
* extra round-trip.
|
|
20
|
+
*/
|
|
21
|
+
policyResolver?: PolicyResolverLike;
|
|
13
22
|
}
|
|
14
23
|
/** The value a frame handler returns (minus the `id` which the channel fills in). */
|
|
15
24
|
type AckBody = Omit<ControlAck, "id">;
|
|
@@ -19,6 +28,26 @@ type AckBody = Omit<ControlAck, "id">;
|
|
|
19
28
|
* `ControlChannelOptions.handle`.
|
|
20
29
|
*/
|
|
21
30
|
export declare function createProvisioner(opts: ProvisionerOptions): (frame: ControlFrame) => Promise<AckBody>;
|
|
31
|
+
export interface AdoptDiscoveredOpenclawAgentsResult {
|
|
32
|
+
adopted: string[];
|
|
33
|
+
skipped: Array<{
|
|
34
|
+
gateway: string;
|
|
35
|
+
openclawAgent?: string;
|
|
36
|
+
reason: string;
|
|
37
|
+
}>;
|
|
38
|
+
failed: Array<{
|
|
39
|
+
gateway: string;
|
|
40
|
+
openclawAgent?: string;
|
|
41
|
+
error: string;
|
|
42
|
+
}>;
|
|
43
|
+
}
|
|
44
|
+
export declare function adoptDiscoveredOpenclawAgents(ctx: {
|
|
45
|
+
gateway: Gateway;
|
|
46
|
+
register?: typeof BotCordClient.register;
|
|
47
|
+
cfg?: DaemonConfig;
|
|
48
|
+
timeoutMs?: number;
|
|
49
|
+
probe?: WsEndpointProbeFn;
|
|
50
|
+
}): Promise<AdoptDiscoveredOpenclawAgentsResult>;
|
|
22
51
|
/**
|
|
23
52
|
* Append `agentId` to the daemon config if not already present. Returns a
|
|
24
53
|
* new config object or `null` if nothing changed (so callers can skip the
|
|
@@ -36,6 +65,89 @@ export declare function removeAgentFromConfig(cfg: DaemonConfig, agentId: string
|
|
|
36
65
|
* gateway already isolates from throwing) and reading the wall clock.
|
|
37
66
|
*/
|
|
38
67
|
export declare function collectRuntimeSnapshot(): ListRuntimesResult;
|
|
68
|
+
/** Maximum number of `endpoints[]` entries persisted per runtime (RFC §3.8.2). */
|
|
69
|
+
export declare const RUNTIME_ENDPOINTS_CAP = 32;
|
|
70
|
+
/** Injection seam for L2 + L3 endpoint probes — kept testable + side-effect-free. */
|
|
71
|
+
export type WsEndpointProbeFn = (args: {
|
|
72
|
+
url: string;
|
|
73
|
+
token?: string;
|
|
74
|
+
timeoutMs: number;
|
|
75
|
+
}) => Promise<{
|
|
76
|
+
ok: boolean;
|
|
77
|
+
version?: string;
|
|
78
|
+
/**
|
|
79
|
+
* L3 — populated when `agents.list` succeeds. `id` is the stable key
|
|
80
|
+
* consumed by route lookups / `openclawAgent`; `name` is display-only.
|
|
81
|
+
*/
|
|
82
|
+
agents?: Array<{
|
|
83
|
+
id: string;
|
|
84
|
+
name?: string;
|
|
85
|
+
workspace?: string;
|
|
86
|
+
model?: {
|
|
87
|
+
name?: string;
|
|
88
|
+
provider?: string;
|
|
89
|
+
};
|
|
90
|
+
}>;
|
|
91
|
+
error?: string;
|
|
92
|
+
}>;
|
|
93
|
+
export declare function probeOpenclawAgents(profile: {
|
|
94
|
+
url: string;
|
|
95
|
+
token?: string;
|
|
96
|
+
tokenFile?: string;
|
|
97
|
+
}, opts?: {
|
|
98
|
+
timeoutMs?: number;
|
|
99
|
+
probe?: WsEndpointProbeFn;
|
|
100
|
+
}): Promise<{
|
|
101
|
+
ok: boolean;
|
|
102
|
+
version?: string;
|
|
103
|
+
agents?: Array<{
|
|
104
|
+
id: string;
|
|
105
|
+
name?: string;
|
|
106
|
+
workspace?: string;
|
|
107
|
+
model?: {
|
|
108
|
+
name?: string;
|
|
109
|
+
provider?: string;
|
|
110
|
+
};
|
|
111
|
+
}>;
|
|
112
|
+
error?: string;
|
|
113
|
+
}>;
|
|
114
|
+
/**
|
|
115
|
+
* Async variant that includes L2 (gateway reachability) and L3 (agent listing)
|
|
116
|
+
* probes for runtimes that talk to external services. Used by the production
|
|
117
|
+
* `list_runtimes` and first-connect snapshot paths.
|
|
118
|
+
*
|
|
119
|
+
* `cfg` is optional so existing callers without a loaded config (e.g. tests)
|
|
120
|
+
* can keep using the sync `collectRuntimeSnapshot()` — when absent, the result
|
|
121
|
+
* is identical to that function.
|
|
122
|
+
*/
|
|
123
|
+
export declare function collectRuntimeSnapshotAsync(opts?: {
|
|
124
|
+
cfg?: {
|
|
125
|
+
openclawGateways?: Array<{
|
|
126
|
+
name: string;
|
|
127
|
+
url: string;
|
|
128
|
+
token?: string;
|
|
129
|
+
tokenFile?: string;
|
|
130
|
+
}>;
|
|
131
|
+
};
|
|
132
|
+
wsProbe?: WsEndpointProbeFn;
|
|
133
|
+
timeoutMs?: number;
|
|
134
|
+
}): Promise<ListRuntimesResult>;
|
|
135
|
+
interface HelloIdentityResult {
|
|
136
|
+
updated: number;
|
|
137
|
+
skipped: number;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Reconcile every agent identity carried by the `hello.agents` snapshot
|
|
141
|
+
* against the on-disk `identity.md`. Best-effort: a malformed entry or a
|
|
142
|
+
* file-system error for one agent never aborts the rest.
|
|
143
|
+
*
|
|
144
|
+
* Identity-snapshot semantics intentionally only touch the metadata
|
|
145
|
+
* line + Bio body — Role/Boundaries paragraphs the user authored locally
|
|
146
|
+
* are preserved (see `applyAgentIdentity`). Missing identity.md files
|
|
147
|
+
* (agent provisioned on a different daemon, or workspace cleared) are
|
|
148
|
+
* silently skipped.
|
|
149
|
+
*/
|
|
150
|
+
export declare function applyHelloIdentitySnapshot(snapshot: AgentIdentitySnapshot[] | undefined): HelloIdentityResult;
|
|
39
151
|
interface ReloadResult {
|
|
40
152
|
reloaded: true;
|
|
41
153
|
added: string[];
|