@dotsetlabs/dotclaw 1.1.0
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/.env.example +54 -0
- package/LICENSE +21 -0
- package/README.md +111 -0
- package/config-examples/groups/global/CLAUDE.md +21 -0
- package/config-examples/groups/main/CLAUDE.md +47 -0
- package/config-examples/mount-allowlist.json +25 -0
- package/config-examples/plugin-http.json +18 -0
- package/config-examples/runtime.json +30 -0
- package/config-examples/tool-budgets.json +24 -0
- package/config-examples/tool-policy.json +51 -0
- package/container/.dockerignore +6 -0
- package/container/Dockerfile +74 -0
- package/container/agent-runner/package-lock.json +92 -0
- package/container/agent-runner/package.json +20 -0
- package/container/agent-runner/src/agent-config.ts +295 -0
- package/container/agent-runner/src/container-protocol.ts +73 -0
- package/container/agent-runner/src/daemon.ts +91 -0
- package/container/agent-runner/src/index.ts +1428 -0
- package/container/agent-runner/src/ipc.ts +321 -0
- package/container/agent-runner/src/memory.ts +336 -0
- package/container/agent-runner/src/prompt-packs.ts +341 -0
- package/container/agent-runner/src/tools.ts +1720 -0
- package/container/agent-runner/tsconfig.json +19 -0
- package/container/build.sh +23 -0
- package/container/skills/agent-browser.md +159 -0
- package/dist/admin-commands.d.ts +7 -0
- package/dist/admin-commands.d.ts.map +1 -0
- package/dist/admin-commands.js +87 -0
- package/dist/admin-commands.js.map +1 -0
- package/dist/agent-context.d.ts +42 -0
- package/dist/agent-context.d.ts.map +1 -0
- package/dist/agent-context.js +92 -0
- package/dist/agent-context.js.map +1 -0
- package/dist/agent-execution.d.ts +68 -0
- package/dist/agent-execution.d.ts.map +1 -0
- package/dist/agent-execution.js +169 -0
- package/dist/agent-execution.js.map +1 -0
- package/dist/agent-semaphore.d.ts +2 -0
- package/dist/agent-semaphore.d.ts.map +1 -0
- package/dist/agent-semaphore.js +52 -0
- package/dist/agent-semaphore.js.map +1 -0
- package/dist/behavior-config.d.ts +14 -0
- package/dist/behavior-config.d.ts.map +1 -0
- package/dist/behavior-config.js +52 -0
- package/dist/behavior-config.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +626 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +31 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +38 -0
- package/dist/config.js.map +1 -0
- package/dist/container-protocol.d.ts +72 -0
- package/dist/container-protocol.d.ts.map +1 -0
- package/dist/container-protocol.js +3 -0
- package/dist/container-protocol.js.map +1 -0
- package/dist/container-runner.d.ts +59 -0
- package/dist/container-runner.d.ts.map +1 -0
- package/dist/container-runner.js +813 -0
- package/dist/container-runner.js.map +1 -0
- package/dist/cost.d.ts +9 -0
- package/dist/cost.d.ts.map +1 -0
- package/dist/cost.js +11 -0
- package/dist/cost.js.map +1 -0
- package/dist/dashboard.d.ts +58 -0
- package/dist/dashboard.d.ts.map +1 -0
- package/dist/dashboard.js +471 -0
- package/dist/dashboard.js.map +1 -0
- package/dist/db.d.ts +99 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +423 -0
- package/dist/db.js.map +1 -0
- package/dist/error-messages.d.ts +17 -0
- package/dist/error-messages.d.ts.map +1 -0
- package/dist/error-messages.js +109 -0
- package/dist/error-messages.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2072 -0
- package/dist/index.js.map +1 -0
- package/dist/locks.d.ts +2 -0
- package/dist/locks.d.ts.map +1 -0
- package/dist/locks.js +26 -0
- package/dist/locks.js.map +1 -0
- package/dist/logger.d.ts +4 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +15 -0
- package/dist/logger.js.map +1 -0
- package/dist/maintenance.d.ts +13 -0
- package/dist/maintenance.d.ts.map +1 -0
- package/dist/maintenance.js +151 -0
- package/dist/maintenance.js.map +1 -0
- package/dist/memory-embeddings.d.ts +13 -0
- package/dist/memory-embeddings.d.ts.map +1 -0
- package/dist/memory-embeddings.js +126 -0
- package/dist/memory-embeddings.js.map +1 -0
- package/dist/memory-recall.d.ts +8 -0
- package/dist/memory-recall.d.ts.map +1 -0
- package/dist/memory-recall.js +127 -0
- package/dist/memory-recall.js.map +1 -0
- package/dist/memory-store.d.ts +149 -0
- package/dist/memory-store.d.ts.map +1 -0
- package/dist/memory-store.js +787 -0
- package/dist/memory-store.js.map +1 -0
- package/dist/metrics.d.ts +12 -0
- package/dist/metrics.d.ts.map +1 -0
- package/dist/metrics.js +134 -0
- package/dist/metrics.js.map +1 -0
- package/dist/model-registry.d.ts +67 -0
- package/dist/model-registry.d.ts.map +1 -0
- package/dist/model-registry.js +230 -0
- package/dist/model-registry.js.map +1 -0
- package/dist/mount-security.d.ts +37 -0
- package/dist/mount-security.d.ts.map +1 -0
- package/dist/mount-security.js +284 -0
- package/dist/mount-security.js.map +1 -0
- package/dist/paths.d.ts +80 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +149 -0
- package/dist/paths.js.map +1 -0
- package/dist/personalization.d.ts +6 -0
- package/dist/personalization.d.ts.map +1 -0
- package/dist/personalization.js +180 -0
- package/dist/personalization.js.map +1 -0
- package/dist/progress.d.ts +15 -0
- package/dist/progress.d.ts.map +1 -0
- package/dist/progress.js +92 -0
- package/dist/progress.js.map +1 -0
- package/dist/runtime-config.d.ts +227 -0
- package/dist/runtime-config.d.ts.map +1 -0
- package/dist/runtime-config.js +297 -0
- package/dist/runtime-config.js.map +1 -0
- package/dist/task-scheduler.d.ts +9 -0
- package/dist/task-scheduler.d.ts.map +1 -0
- package/dist/task-scheduler.js +195 -0
- package/dist/task-scheduler.js.map +1 -0
- package/dist/telegram-format.d.ts +3 -0
- package/dist/telegram-format.d.ts.map +1 -0
- package/dist/telegram-format.js +200 -0
- package/dist/telegram-format.js.map +1 -0
- package/dist/tool-budgets.d.ts +16 -0
- package/dist/tool-budgets.d.ts.map +1 -0
- package/dist/tool-budgets.js +83 -0
- package/dist/tool-budgets.js.map +1 -0
- package/dist/tool-policy.d.ts +18 -0
- package/dist/tool-policy.d.ts.map +1 -0
- package/dist/tool-policy.js +84 -0
- package/dist/tool-policy.js.map +1 -0
- package/dist/trace-writer.d.ts +39 -0
- package/dist/trace-writer.d.ts.map +1 -0
- package/dist/trace-writer.js +27 -0
- package/dist/trace-writer.js.map +1 -0
- package/dist/types.d.ts +81 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +4 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +30 -0
- package/dist/utils.js.map +1 -0
- package/launchd/com.dotclaw.plist +32 -0
- package/package.json +89 -0
- package/scripts/autotune.js +53 -0
- package/scripts/bootstrap.js +348 -0
- package/scripts/configure.js +200 -0
- package/scripts/doctor.js +164 -0
- package/scripts/init.js +209 -0
- package/scripts/install.sh +219 -0
- package/systemd/dotclaw.service +22 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
|
|
3
|
+
export type AgentRuntimeConfig = {
|
|
4
|
+
defaultModel: string;
|
|
5
|
+
daemonPollMs: number;
|
|
6
|
+
agent: {
|
|
7
|
+
assistantName: string;
|
|
8
|
+
openrouter: {
|
|
9
|
+
timeoutMs: number;
|
|
10
|
+
retry: boolean;
|
|
11
|
+
siteUrl: string;
|
|
12
|
+
siteName: string;
|
|
13
|
+
};
|
|
14
|
+
promptPacks: {
|
|
15
|
+
enabled: boolean;
|
|
16
|
+
maxChars: number;
|
|
17
|
+
maxDemos: number;
|
|
18
|
+
canaryRate: number;
|
|
19
|
+
};
|
|
20
|
+
context: {
|
|
21
|
+
maxContextTokens: number;
|
|
22
|
+
compactionTriggerTokens: number;
|
|
23
|
+
recentContextTokens: number;
|
|
24
|
+
summaryUpdateEveryMessages: number;
|
|
25
|
+
maxOutputTokens: number;
|
|
26
|
+
summaryMaxOutputTokens: number;
|
|
27
|
+
temperature: number;
|
|
28
|
+
maxContextMessageTokens: number;
|
|
29
|
+
};
|
|
30
|
+
memory: {
|
|
31
|
+
maxResults: number;
|
|
32
|
+
maxTokens: number;
|
|
33
|
+
extraction: {
|
|
34
|
+
enabled: boolean;
|
|
35
|
+
async: boolean;
|
|
36
|
+
maxMessages: number;
|
|
37
|
+
maxOutputTokens: number;
|
|
38
|
+
};
|
|
39
|
+
archiveSync: boolean;
|
|
40
|
+
extractScheduled: boolean;
|
|
41
|
+
};
|
|
42
|
+
models: {
|
|
43
|
+
summary: string;
|
|
44
|
+
memory: string;
|
|
45
|
+
planner: string;
|
|
46
|
+
responseValidation: string;
|
|
47
|
+
toolSummary: string;
|
|
48
|
+
};
|
|
49
|
+
planner: {
|
|
50
|
+
enabled: boolean;
|
|
51
|
+
mode: string;
|
|
52
|
+
minTokens: number;
|
|
53
|
+
triggerRegex: string;
|
|
54
|
+
maxOutputTokens: number;
|
|
55
|
+
temperature: number;
|
|
56
|
+
};
|
|
57
|
+
responseValidation: {
|
|
58
|
+
enabled: boolean;
|
|
59
|
+
maxOutputTokens: number;
|
|
60
|
+
temperature: number;
|
|
61
|
+
maxRetries: number;
|
|
62
|
+
allowToolCalls: boolean;
|
|
63
|
+
};
|
|
64
|
+
tools: {
|
|
65
|
+
maxToolSteps: number;
|
|
66
|
+
outputLimitBytes: number;
|
|
67
|
+
enableBash: boolean;
|
|
68
|
+
enableWebSearch: boolean;
|
|
69
|
+
enableWebFetch: boolean;
|
|
70
|
+
webfetch: {
|
|
71
|
+
blockPrivate: boolean;
|
|
72
|
+
allowlist: string[];
|
|
73
|
+
blocklist: string[];
|
|
74
|
+
maxBytes: number;
|
|
75
|
+
timeoutMs: number;
|
|
76
|
+
};
|
|
77
|
+
websearch: {
|
|
78
|
+
timeoutMs: number;
|
|
79
|
+
};
|
|
80
|
+
bash: {
|
|
81
|
+
timeoutMs: number;
|
|
82
|
+
outputLimitBytes: number;
|
|
83
|
+
};
|
|
84
|
+
grepMaxFileBytes: number;
|
|
85
|
+
plugin: {
|
|
86
|
+
dirs: string[];
|
|
87
|
+
maxBytes: number;
|
|
88
|
+
httpTimeoutMs: number;
|
|
89
|
+
};
|
|
90
|
+
toolSummary: {
|
|
91
|
+
enabled: boolean;
|
|
92
|
+
maxBytes: number;
|
|
93
|
+
maxOutputTokens: number;
|
|
94
|
+
tools: string[];
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
streaming: {
|
|
98
|
+
minIntervalMs: number;
|
|
99
|
+
minChars: number;
|
|
100
|
+
};
|
|
101
|
+
ipc: {
|
|
102
|
+
requestTimeoutMs: number;
|
|
103
|
+
requestPollMs: number;
|
|
104
|
+
};
|
|
105
|
+
tokenEstimate: {
|
|
106
|
+
tokensPerChar: number;
|
|
107
|
+
tokensPerMessage: number;
|
|
108
|
+
tokensPerRequest: number;
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const CONFIG_PATH = '/workspace/config/runtime.json';
|
|
114
|
+
const DEFAULT_DEFAULT_MODEL = 'moonshotai/kimi-k2.5';
|
|
115
|
+
const DEFAULT_DAEMON_POLL_MS = 200;
|
|
116
|
+
|
|
117
|
+
const DEFAULT_AGENT_CONFIG: AgentRuntimeConfig['agent'] = {
|
|
118
|
+
assistantName: 'Rain',
|
|
119
|
+
openrouter: {
|
|
120
|
+
timeoutMs: 180_000,
|
|
121
|
+
retry: true,
|
|
122
|
+
siteUrl: '',
|
|
123
|
+
siteName: ''
|
|
124
|
+
},
|
|
125
|
+
promptPacks: {
|
|
126
|
+
enabled: true,
|
|
127
|
+
maxChars: 6000,
|
|
128
|
+
maxDemos: 4,
|
|
129
|
+
canaryRate: 0.1
|
|
130
|
+
},
|
|
131
|
+
context: {
|
|
132
|
+
maxContextTokens: 24_000,
|
|
133
|
+
compactionTriggerTokens: 20_000,
|
|
134
|
+
recentContextTokens: 8000,
|
|
135
|
+
summaryUpdateEveryMessages: 20,
|
|
136
|
+
maxOutputTokens: 1024,
|
|
137
|
+
summaryMaxOutputTokens: 600,
|
|
138
|
+
temperature: 0.2,
|
|
139
|
+
maxContextMessageTokens: 3000
|
|
140
|
+
},
|
|
141
|
+
memory: {
|
|
142
|
+
maxResults: 6,
|
|
143
|
+
maxTokens: 2000,
|
|
144
|
+
extraction: {
|
|
145
|
+
enabled: true,
|
|
146
|
+
async: true,
|
|
147
|
+
maxMessages: 4,
|
|
148
|
+
maxOutputTokens: 200
|
|
149
|
+
},
|
|
150
|
+
archiveSync: true,
|
|
151
|
+
extractScheduled: false
|
|
152
|
+
},
|
|
153
|
+
models: {
|
|
154
|
+
summary: 'openai/gpt-5-nano',
|
|
155
|
+
memory: 'openai/gpt-5-mini',
|
|
156
|
+
planner: 'openai/gpt-5-nano',
|
|
157
|
+
responseValidation: 'openai/gpt-5-nano',
|
|
158
|
+
toolSummary: 'openai/gpt-5-nano'
|
|
159
|
+
},
|
|
160
|
+
planner: {
|
|
161
|
+
enabled: true,
|
|
162
|
+
mode: 'auto',
|
|
163
|
+
minTokens: 600,
|
|
164
|
+
triggerRegex: '(plan|steps|roadmap|research|design|architecture|spec|strategy)',
|
|
165
|
+
maxOutputTokens: 200,
|
|
166
|
+
temperature: 0.2
|
|
167
|
+
},
|
|
168
|
+
responseValidation: {
|
|
169
|
+
enabled: true,
|
|
170
|
+
maxOutputTokens: 120,
|
|
171
|
+
temperature: 0,
|
|
172
|
+
maxRetries: 1,
|
|
173
|
+
allowToolCalls: false
|
|
174
|
+
},
|
|
175
|
+
tools: {
|
|
176
|
+
maxToolSteps: 24,
|
|
177
|
+
outputLimitBytes: 400_000,
|
|
178
|
+
enableBash: true,
|
|
179
|
+
enableWebSearch: true,
|
|
180
|
+
enableWebFetch: true,
|
|
181
|
+
webfetch: {
|
|
182
|
+
blockPrivate: true,
|
|
183
|
+
allowlist: [],
|
|
184
|
+
blocklist: ['localhost', '127.0.0.1'],
|
|
185
|
+
maxBytes: 300_000,
|
|
186
|
+
timeoutMs: 20_000
|
|
187
|
+
},
|
|
188
|
+
websearch: {
|
|
189
|
+
timeoutMs: 20_000
|
|
190
|
+
},
|
|
191
|
+
bash: {
|
|
192
|
+
timeoutMs: 120_000,
|
|
193
|
+
outputLimitBytes: 200_000
|
|
194
|
+
},
|
|
195
|
+
grepMaxFileBytes: 1_000_000,
|
|
196
|
+
plugin: {
|
|
197
|
+
dirs: [],
|
|
198
|
+
maxBytes: 800_000,
|
|
199
|
+
httpTimeoutMs: 20_000
|
|
200
|
+
},
|
|
201
|
+
toolSummary: {
|
|
202
|
+
enabled: true,
|
|
203
|
+
maxBytes: 60_000,
|
|
204
|
+
maxOutputTokens: 400,
|
|
205
|
+
tools: ['WebFetch']
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
streaming: {
|
|
209
|
+
minIntervalMs: 800,
|
|
210
|
+
minChars: 120
|
|
211
|
+
},
|
|
212
|
+
ipc: {
|
|
213
|
+
requestTimeoutMs: 6000,
|
|
214
|
+
requestPollMs: 150
|
|
215
|
+
},
|
|
216
|
+
tokenEstimate: {
|
|
217
|
+
tokensPerChar: 0.25,
|
|
218
|
+
tokensPerMessage: 3,
|
|
219
|
+
tokensPerRequest: 0
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
let cachedConfig: AgentRuntimeConfig | null = null;
|
|
224
|
+
|
|
225
|
+
function cloneConfig<T>(value: T): T {
|
|
226
|
+
return JSON.parse(JSON.stringify(value)) as T;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
230
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function mergeDefaults<T>(base: T, overrides: unknown): T {
|
|
234
|
+
if (!isPlainObject(overrides)) return cloneConfig(base);
|
|
235
|
+
const result = cloneConfig(base) as Record<string, unknown>;
|
|
236
|
+
const baseObj = base as Record<string, unknown>;
|
|
237
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
238
|
+
const current = baseObj[key];
|
|
239
|
+
if (isPlainObject(current) && isPlainObject(value)) {
|
|
240
|
+
result[key] = mergeDefaults(current, value);
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
if (Array.isArray(current) && Array.isArray(value)) {
|
|
244
|
+
result[key] = value;
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
if (typeof value === typeof current) {
|
|
248
|
+
result[key] = value as unknown;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return result as T;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function readJson(filePath: string): unknown {
|
|
255
|
+
try {
|
|
256
|
+
if (!fs.existsSync(filePath)) return null;
|
|
257
|
+
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
258
|
+
if (!raw.trim()) return null;
|
|
259
|
+
return JSON.parse(raw);
|
|
260
|
+
} catch {
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export function loadAgentConfig(): AgentRuntimeConfig {
|
|
266
|
+
if (cachedConfig) return cachedConfig;
|
|
267
|
+
const raw = readJson(CONFIG_PATH);
|
|
268
|
+
|
|
269
|
+
let defaultModel = DEFAULT_DEFAULT_MODEL;
|
|
270
|
+
let daemonPollMs = DEFAULT_DAEMON_POLL_MS;
|
|
271
|
+
let agentOverrides: unknown = null;
|
|
272
|
+
|
|
273
|
+
if (isPlainObject(raw)) {
|
|
274
|
+
const host = raw.host;
|
|
275
|
+
if (isPlainObject(host)) {
|
|
276
|
+
if (typeof host.defaultModel === 'string' && host.defaultModel.trim()) {
|
|
277
|
+
defaultModel = host.defaultModel.trim();
|
|
278
|
+
}
|
|
279
|
+
const container = host.container;
|
|
280
|
+
if (isPlainObject(container) && typeof container.daemonPollMs === 'number') {
|
|
281
|
+
daemonPollMs = container.daemonPollMs;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
if (isPlainObject(raw.agent)) {
|
|
285
|
+
agentOverrides = raw.agent;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
cachedConfig = {
|
|
290
|
+
defaultModel,
|
|
291
|
+
daemonPollMs,
|
|
292
|
+
agent: mergeDefaults(DEFAULT_AGENT_CONFIG, agentOverrides)
|
|
293
|
+
};
|
|
294
|
+
return cachedConfig;
|
|
295
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
export const OUTPUT_START_MARKER = '---DOTCLAW_OUTPUT_START---';
|
|
2
|
+
export const OUTPUT_END_MARKER = '---DOTCLAW_OUTPUT_END---';
|
|
3
|
+
|
|
4
|
+
export interface ContainerInput {
|
|
5
|
+
prompt: string;
|
|
6
|
+
sessionId?: string;
|
|
7
|
+
groupFolder: string;
|
|
8
|
+
chatJid: string;
|
|
9
|
+
isMain: boolean;
|
|
10
|
+
isScheduledTask?: boolean;
|
|
11
|
+
isBackgroundTask?: boolean;
|
|
12
|
+
taskId?: string;
|
|
13
|
+
userId?: string;
|
|
14
|
+
userName?: string;
|
|
15
|
+
memoryRecall?: string[];
|
|
16
|
+
userProfile?: string | null;
|
|
17
|
+
memoryStats?: {
|
|
18
|
+
total: number;
|
|
19
|
+
user: number;
|
|
20
|
+
group: number;
|
|
21
|
+
global: number;
|
|
22
|
+
};
|
|
23
|
+
tokenEstimate?: {
|
|
24
|
+
tokens_per_char: number;
|
|
25
|
+
tokens_per_message: number;
|
|
26
|
+
tokens_per_request: number;
|
|
27
|
+
};
|
|
28
|
+
toolReliability?: Array<{
|
|
29
|
+
name: string;
|
|
30
|
+
success_rate: number;
|
|
31
|
+
count: number;
|
|
32
|
+
avg_duration_ms: number | null;
|
|
33
|
+
}>;
|
|
34
|
+
behaviorConfig?: Record<string, unknown>;
|
|
35
|
+
toolPolicy?: Record<string, unknown>;
|
|
36
|
+
modelOverride?: string;
|
|
37
|
+
modelContextTokens?: number;
|
|
38
|
+
modelMaxOutputTokens?: number;
|
|
39
|
+
modelTemperature?: number;
|
|
40
|
+
streaming?: {
|
|
41
|
+
enabled?: boolean;
|
|
42
|
+
draftId?: number;
|
|
43
|
+
minIntervalMs?: number;
|
|
44
|
+
minChars?: number;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface ContainerOutput {
|
|
49
|
+
status: 'success' | 'error';
|
|
50
|
+
result: string | null;
|
|
51
|
+
newSessionId?: string;
|
|
52
|
+
error?: string;
|
|
53
|
+
model?: string;
|
|
54
|
+
prompt_pack_versions?: Record<string, string>;
|
|
55
|
+
memory_summary?: string;
|
|
56
|
+
memory_facts?: string[];
|
|
57
|
+
tokens_prompt?: number;
|
|
58
|
+
tokens_completion?: number;
|
|
59
|
+
memory_recall_count?: number;
|
|
60
|
+
session_recall_count?: number;
|
|
61
|
+
memory_items_upserted?: number;
|
|
62
|
+
memory_items_extracted?: number;
|
|
63
|
+
tool_calls?: Array<{
|
|
64
|
+
name: string;
|
|
65
|
+
args?: unknown;
|
|
66
|
+
ok: boolean;
|
|
67
|
+
duration_ms?: number;
|
|
68
|
+
error?: string;
|
|
69
|
+
output_bytes?: number;
|
|
70
|
+
output_truncated?: boolean;
|
|
71
|
+
}>;
|
|
72
|
+
latency_ms?: number;
|
|
73
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { runAgentOnce } from './index.js';
|
|
4
|
+
import { loadAgentConfig } from './agent-config.js';
|
|
5
|
+
import type { ContainerInput } from './container-protocol.js';
|
|
6
|
+
|
|
7
|
+
const REQUESTS_DIR = '/workspace/ipc/agent_requests';
|
|
8
|
+
const RESPONSES_DIR = '/workspace/ipc/agent_responses';
|
|
9
|
+
const HEARTBEAT_FILE = '/workspace/ipc/heartbeat';
|
|
10
|
+
const POLL_MS = loadAgentConfig().daemonPollMs;
|
|
11
|
+
|
|
12
|
+
function log(message: string): void {
|
|
13
|
+
console.error(`[agent-daemon] ${message}`);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function ensureDirs(): void {
|
|
17
|
+
fs.mkdirSync(REQUESTS_DIR, { recursive: true });
|
|
18
|
+
fs.mkdirSync(RESPONSES_DIR, { recursive: true });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function writeHeartbeat(): void {
|
|
22
|
+
try {
|
|
23
|
+
fs.writeFileSync(HEARTBEAT_FILE, Date.now().toString());
|
|
24
|
+
} catch {
|
|
25
|
+
// Ignore heartbeat write errors
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function processRequests(): Promise<void> {
|
|
30
|
+
const files = fs.readdirSync(REQUESTS_DIR).filter(file => file.endsWith('.json'));
|
|
31
|
+
for (const file of files) {
|
|
32
|
+
const filePath = path.join(REQUESTS_DIR, file);
|
|
33
|
+
let requestId = file.replace('.json', '');
|
|
34
|
+
try {
|
|
35
|
+
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
36
|
+
const payload = JSON.parse(raw) as { id?: string; input?: unknown };
|
|
37
|
+
requestId = payload.id || requestId;
|
|
38
|
+
const input = payload.input || payload;
|
|
39
|
+
if (!isContainerInput(input)) {
|
|
40
|
+
throw new Error('Invalid agent request payload');
|
|
41
|
+
}
|
|
42
|
+
const output = await runAgentOnce(input);
|
|
43
|
+
const responsePath = path.join(RESPONSES_DIR, `${requestId}.json`);
|
|
44
|
+
fs.writeFileSync(responsePath, JSON.stringify(output));
|
|
45
|
+
fs.unlinkSync(filePath);
|
|
46
|
+
} catch (err) {
|
|
47
|
+
log(`Failed processing request ${requestId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
48
|
+
const responsePath = path.join(RESPONSES_DIR, `${requestId}.json`);
|
|
49
|
+
fs.writeFileSync(responsePath, JSON.stringify({
|
|
50
|
+
status: 'error',
|
|
51
|
+
result: null,
|
|
52
|
+
error: err instanceof Error ? err.message : String(err)
|
|
53
|
+
}));
|
|
54
|
+
try {
|
|
55
|
+
fs.unlinkSync(filePath);
|
|
56
|
+
} catch {
|
|
57
|
+
// ignore cleanup failure
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function isContainerInput(value: unknown): value is ContainerInput {
|
|
64
|
+
if (!value || typeof value !== 'object') return false;
|
|
65
|
+
const record = value as Record<string, unknown>;
|
|
66
|
+
return typeof record.prompt === 'string'
|
|
67
|
+
&& typeof record.groupFolder === 'string'
|
|
68
|
+
&& typeof record.chatJid === 'string'
|
|
69
|
+
&& typeof record.isMain === 'boolean';
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function loop(): Promise<void> {
|
|
73
|
+
ensureDirs();
|
|
74
|
+
log('Daemon started');
|
|
75
|
+
while (true) {
|
|
76
|
+
// Write heartbeat at the start of each loop iteration
|
|
77
|
+
writeHeartbeat();
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
await processRequests();
|
|
81
|
+
} catch (err) {
|
|
82
|
+
log(`Daemon loop error: ${err instanceof Error ? err.message : String(err)}`);
|
|
83
|
+
}
|
|
84
|
+
await new Promise(resolve => setTimeout(resolve, POLL_MS));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
loop().catch(err => {
|
|
89
|
+
log(`Daemon fatal error: ${err instanceof Error ? err.message : String(err)}`);
|
|
90
|
+
process.exit(1);
|
|
91
|
+
});
|