@dotsetlabs/dotclaw 2.1.0 → 2.2.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 +12 -0
- package/README.md +5 -2
- package/config-examples/runtime.json +46 -5
- package/config-examples/tool-budgets.json +1 -1
- package/config-examples/tool-policy.json +1 -1
- package/container/Dockerfile +5 -1
- package/container/agent-runner/package.json +1 -1
- package/container/agent-runner/src/agent-config.ts +65 -15
- package/container/agent-runner/src/container-protocol.ts +6 -0
- package/container/agent-runner/src/daemon.ts +18 -5
- package/container/agent-runner/src/index.ts +416 -240
- package/container/agent-runner/src/ipc.ts +76 -1
- package/container/agent-runner/src/mcp-registry.ts +11 -0
- package/container/agent-runner/src/memory.ts +139 -1
- package/container/agent-runner/src/process-registry.ts +257 -0
- package/container/agent-runner/src/system-prompt.ts +311 -0
- package/container/agent-runner/src/tools.ts +382 -29
- package/container/agent-runner/src/tts.ts +42 -0
- package/dist/agent-context.d.ts +1 -0
- package/dist/agent-context.d.ts.map +1 -1
- package/dist/agent-context.js +6 -3
- package/dist/agent-context.js.map +1 -1
- package/dist/agent-execution.d.ts +1 -0
- package/dist/agent-execution.d.ts.map +1 -1
- package/dist/agent-execution.js +11 -4
- package/dist/agent-execution.js.map +1 -1
- package/dist/container-protocol.d.ts +8 -0
- package/dist/container-protocol.d.ts.map +1 -1
- package/dist/container-runner.d.ts.map +1 -1
- package/dist/container-runner.js +44 -8
- package/dist/container-runner.js.map +1 -1
- package/dist/index.js +53 -6
- package/dist/index.js.map +1 -1
- package/dist/ipc-dispatcher.d.ts.map +1 -1
- package/dist/ipc-dispatcher.js +336 -6
- package/dist/ipc-dispatcher.js.map +1 -1
- package/dist/memory-recall.d.ts +1 -0
- package/dist/memory-recall.d.ts.map +1 -1
- package/dist/memory-recall.js +3 -0
- package/dist/memory-recall.js.map +1 -1
- package/dist/memory-store.d.ts.map +1 -1
- package/dist/memory-store.js +5 -3
- package/dist/memory-store.js.map +1 -1
- package/dist/message-pipeline.d.ts.map +1 -1
- package/dist/message-pipeline.js +30 -9
- package/dist/message-pipeline.js.map +1 -1
- package/dist/model-registry.d.ts +15 -0
- package/dist/model-registry.d.ts.map +1 -1
- package/dist/model-registry.js +56 -12
- package/dist/model-registry.js.map +1 -1
- package/dist/providers/telegram/telegram-provider.d.ts +1 -0
- package/dist/providers/telegram/telegram-provider.d.ts.map +1 -1
- package/dist/providers/telegram/telegram-provider.js +14 -0
- package/dist/providers/telegram/telegram-provider.js.map +1 -1
- package/dist/request-router.d.ts +0 -1
- package/dist/request-router.d.ts.map +1 -1
- package/dist/request-router.js +18 -6
- package/dist/request-router.js.map +1 -1
- package/dist/runtime-config.d.ts +14 -0
- package/dist/runtime-config.d.ts.map +1 -1
- package/dist/runtime-config.js +64 -16
- package/dist/runtime-config.js.map +1 -1
- package/dist/task-scheduler.d.ts.map +1 -1
- package/dist/task-scheduler.js +3 -5
- package/dist/task-scheduler.js.map +1 -1
- package/dist/tool-budgets.js +1 -1
- package/dist/tool-budgets.js.map +1 -1
- package/dist/tool-policy.d.ts.map +1 -1
- package/dist/tool-policy.js +13 -3
- package/dist/tool-policy.js.map +1 -1
- package/dist/webhook.d.ts +14 -0
- package/dist/webhook.d.ts.map +1 -0
- package/dist/webhook.js +169 -0
- package/dist/webhook.js.map +1 -0
- package/package.json +3 -2
package/.env.example
CHANGED
|
@@ -27,6 +27,12 @@ TZ=America/New_York
|
|
|
27
27
|
# GitHub Personal Access Token (enables gh CLI in containers)
|
|
28
28
|
# GH_TOKEN=ghp_your_token_here
|
|
29
29
|
|
|
30
|
+
# OpenAI API key (enables OpenAI TTS provider; falls back to OPENROUTER_API_KEY)
|
|
31
|
+
# OPENAI_API_KEY=sk-replace-with-openai-key
|
|
32
|
+
|
|
33
|
+
# Override vision model for the AnalyzeImage tool (defaults to openai/gpt-4o)
|
|
34
|
+
# DOTCLAW_VISION_MODEL=openai/gpt-4o
|
|
35
|
+
|
|
30
36
|
# --- Optional: System (set in shell before starting) ---
|
|
31
37
|
# Override DotClaw home directory (defaults to ~/.dotclaw)
|
|
32
38
|
# DOTCLAW_HOME=~/.dotclaw
|
|
@@ -39,12 +45,18 @@ TZ=America/New_York
|
|
|
39
45
|
# Required for non-interactive bootstrap
|
|
40
46
|
# DOTCLAW_BOOTSTRAP_CHAT_ID=123456789
|
|
41
47
|
|
|
48
|
+
# Explicit provider selection (telegram or discord)
|
|
49
|
+
# DOTCLAW_BOOTSTRAP_PROVIDER=telegram
|
|
50
|
+
|
|
42
51
|
# Optional bootstrap defaults
|
|
43
52
|
# DOTCLAW_BOOTSTRAP_GROUP_NAME=main
|
|
44
53
|
# DOTCLAW_BOOTSTRAP_GROUP_FOLDER=main
|
|
45
54
|
# DOTCLAW_BOOTSTRAP_BUILD=true
|
|
46
55
|
# DOTCLAW_BOOTSTRAP_SELF_CHECK=true
|
|
47
56
|
|
|
57
|
+
# Required for non-interactive configure
|
|
58
|
+
# DOTCLAW_CONFIGURE_CHAT_ID=123456789
|
|
59
|
+
|
|
48
60
|
# --- Optional: autotune (advanced) ---
|
|
49
61
|
# Autotune uses dotenv, so these can live in .env if you run `npm run autotune`.
|
|
50
62
|
# AUTOTUNE_TRACE_DIR=~/.dotclaw/traces
|
package/README.md
CHANGED
|
@@ -120,10 +120,13 @@ Or see:
|
|
|
120
120
|
|
|
121
121
|
```bash
|
|
122
122
|
npm run dev # Run with hot reload
|
|
123
|
-
npm run
|
|
123
|
+
npm run dev:up # Full dev cycle: rebuild container + kill stale daemons + start dev
|
|
124
|
+
npm run dev:down # Remove all running dotclaw agent containers
|
|
125
|
+
npm run build # Compile TypeScript (host)
|
|
126
|
+
npm run build:all # Build both host and container
|
|
124
127
|
npm run lint # Run ESLint
|
|
125
128
|
npm test # Run tests
|
|
126
|
-
|
|
129
|
+
dotclaw build # Rebuild agent container (or: ./container/build.sh)
|
|
127
130
|
```
|
|
128
131
|
|
|
129
132
|
## License
|
|
@@ -15,6 +15,9 @@
|
|
|
15
15
|
"port": 3002
|
|
16
16
|
},
|
|
17
17
|
"memory": {
|
|
18
|
+
"recall": {
|
|
19
|
+
"minScore": 0.35
|
|
20
|
+
},
|
|
18
21
|
"embeddings": {
|
|
19
22
|
"enabled": true
|
|
20
23
|
}
|
|
@@ -22,32 +25,70 @@
|
|
|
22
25
|
"routing": {
|
|
23
26
|
"model": "moonshotai/kimi-k2.5",
|
|
24
27
|
"fallbacks": ["anthropic/claude-sonnet-4-5", "openai/gpt-4.1"],
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
+
"allowedModels": [],
|
|
29
|
+
"maxOutputTokens": 0,
|
|
30
|
+
"maxToolSteps": 200,
|
|
31
|
+
"temperature": 0.6,
|
|
28
32
|
"recallMaxResults": 8,
|
|
29
33
|
"recallMaxTokens": 1500
|
|
30
34
|
},
|
|
35
|
+
"webhook": {
|
|
36
|
+
"enabled": false,
|
|
37
|
+
"port": 3003,
|
|
38
|
+
"token": ""
|
|
39
|
+
},
|
|
31
40
|
"streaming": {
|
|
32
41
|
"enabled": true,
|
|
33
42
|
"chunkFlushIntervalMs": 200,
|
|
34
43
|
"editIntervalMs": 400,
|
|
35
44
|
"maxEditLength": 3800
|
|
36
45
|
},
|
|
46
|
+
"telegram": {
|
|
47
|
+
"enabled": true
|
|
48
|
+
},
|
|
49
|
+
"discord": {
|
|
50
|
+
"enabled": false
|
|
51
|
+
},
|
|
37
52
|
"toolBudgets": {
|
|
38
53
|
"enabled": false
|
|
39
54
|
}
|
|
40
55
|
},
|
|
41
56
|
"agent": {
|
|
42
57
|
"assistantName": "Rain",
|
|
43
|
-
"reasoning": { "effort": "
|
|
58
|
+
"reasoning": { "effort": "medium" },
|
|
59
|
+
"context": {
|
|
60
|
+
"maxHistoryTurns": 40,
|
|
61
|
+
"contextPruning": {
|
|
62
|
+
"softTrimMaxChars": 4000,
|
|
63
|
+
"softTrimHeadChars": 1500,
|
|
64
|
+
"softTrimTailChars": 1500,
|
|
65
|
+
"keepLastAssistant": 3
|
|
66
|
+
}
|
|
67
|
+
},
|
|
44
68
|
"promptPacks": {
|
|
45
69
|
"enabled": true
|
|
46
70
|
},
|
|
47
71
|
"tools": {
|
|
48
72
|
"enableBash": true,
|
|
49
73
|
"enableWebSearch": true,
|
|
50
|
-
"enableWebFetch": true
|
|
74
|
+
"enableWebFetch": true,
|
|
75
|
+
"bash": {
|
|
76
|
+
"timeoutMs": 600000
|
|
77
|
+
},
|
|
78
|
+
"process": {
|
|
79
|
+
"maxSessions": 16,
|
|
80
|
+
"maxOutputBytes": 1048576,
|
|
81
|
+
"defaultTimeoutMs": 1800000
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
"tts": {
|
|
85
|
+
"provider": "edge-tts",
|
|
86
|
+
"openaiModel": "tts-1",
|
|
87
|
+
"openaiVoice": "alloy"
|
|
88
|
+
},
|
|
89
|
+
"mcp": {
|
|
90
|
+
"enabled": true,
|
|
91
|
+
"servers": []
|
|
51
92
|
}
|
|
52
93
|
}
|
|
53
94
|
}
|
package/container/Dockerfile
CHANGED
|
@@ -37,6 +37,7 @@ RUN apt-get update && apt-get install -y \
|
|
|
37
37
|
tree \
|
|
38
38
|
ripgrep \
|
|
39
39
|
graphviz \
|
|
40
|
+
poppler-utils \
|
|
40
41
|
&& rm -rf /var/lib/apt/lists/*
|
|
41
42
|
|
|
42
43
|
# Install GitHub CLI (not in standard Debian repos)
|
|
@@ -51,7 +52,7 @@ RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \
|
|
|
51
52
|
RUN echo 'node ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/node && chmod 0440 /etc/sudoers.d/node
|
|
52
53
|
|
|
53
54
|
# Install common Python packages
|
|
54
|
-
RUN pip3 install --break-system-packages pandas numpy requests beautifulsoup4 matplotlib Pillow openpyxl pyyaml tabulate chardet
|
|
55
|
+
RUN pip3 install --break-system-packages pandas numpy requests beautifulsoup4 matplotlib Pillow openpyxl pyyaml tabulate chardet pdfminer.six httpx lxml cssselect python-docx python-pptx
|
|
55
56
|
|
|
56
57
|
# Set Chromium path for agent-browser
|
|
57
58
|
ENV AGENT_BROWSER_EXECUTABLE_PATH=/usr/bin/chromium
|
|
@@ -60,6 +61,9 @@ ENV PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/chromium
|
|
|
60
61
|
# Install agent-browser globally
|
|
61
62
|
RUN npm install -g agent-browser
|
|
62
63
|
|
|
64
|
+
# Enable pnpm via corepack (cross-platform lockfiles)
|
|
65
|
+
RUN corepack enable && corepack prepare pnpm@latest --activate
|
|
66
|
+
|
|
63
67
|
# Create app directory
|
|
64
68
|
WORKDIR /app
|
|
65
69
|
|
|
@@ -27,13 +27,19 @@ export type AgentRuntimeConfig = {
|
|
|
27
27
|
summaryMaxOutputTokens: number;
|
|
28
28
|
temperature: number;
|
|
29
29
|
maxContextMessageTokens: number;
|
|
30
|
+
maxHistoryTurns: number;
|
|
31
|
+
contextPruning: {
|
|
32
|
+
softTrimMaxChars: number;
|
|
33
|
+
softTrimHeadChars: number;
|
|
34
|
+
softTrimTailChars: number;
|
|
35
|
+
keepLastAssistant: number;
|
|
36
|
+
};
|
|
30
37
|
};
|
|
31
38
|
memory: {
|
|
32
39
|
maxResults: number;
|
|
33
40
|
maxTokens: number;
|
|
34
41
|
extraction: {
|
|
35
42
|
enabled: boolean;
|
|
36
|
-
async: boolean;
|
|
37
43
|
maxMessages: number;
|
|
38
44
|
maxOutputTokens: number;
|
|
39
45
|
};
|
|
@@ -98,6 +104,9 @@ export type AgentRuntimeConfig = {
|
|
|
98
104
|
model: string;
|
|
99
105
|
baseUrl: string;
|
|
100
106
|
defaultVoice: string;
|
|
107
|
+
provider: 'edge-tts' | 'openai';
|
|
108
|
+
openaiModel: string;
|
|
109
|
+
openaiVoice: string;
|
|
101
110
|
};
|
|
102
111
|
browser: {
|
|
103
112
|
enabled: boolean;
|
|
@@ -124,6 +133,11 @@ export type AgentRuntimeConfig = {
|
|
|
124
133
|
maxSkills: number;
|
|
125
134
|
maxSummaryChars: number;
|
|
126
135
|
};
|
|
136
|
+
process: {
|
|
137
|
+
maxSessions: number;
|
|
138
|
+
maxOutputBytes: number;
|
|
139
|
+
defaultTimeoutMs: number;
|
|
140
|
+
};
|
|
127
141
|
};
|
|
128
142
|
};
|
|
129
143
|
|
|
@@ -147,21 +161,27 @@ const DEFAULT_AGENT_CONFIG: AgentRuntimeConfig['agent'] = {
|
|
|
147
161
|
canaryRate: 0.1
|
|
148
162
|
},
|
|
149
163
|
context: {
|
|
150
|
-
maxContextTokens:
|
|
151
|
-
compactionTriggerTokens:
|
|
164
|
+
maxContextTokens: 128_000,
|
|
165
|
+
compactionTriggerTokens: 120_000,
|
|
152
166
|
recentContextTokens: 8000,
|
|
153
167
|
summaryUpdateEveryMessages: 20,
|
|
154
|
-
maxOutputTokens:
|
|
168
|
+
maxOutputTokens: 8192,
|
|
155
169
|
summaryMaxOutputTokens: 2048,
|
|
156
|
-
temperature: 0.
|
|
157
|
-
maxContextMessageTokens:
|
|
170
|
+
temperature: 0.6,
|
|
171
|
+
maxContextMessageTokens: 4000,
|
|
172
|
+
maxHistoryTurns: 40,
|
|
173
|
+
contextPruning: {
|
|
174
|
+
softTrimMaxChars: 4_000,
|
|
175
|
+
softTrimHeadChars: 1_500,
|
|
176
|
+
softTrimTailChars: 1_500,
|
|
177
|
+
keepLastAssistant: 3
|
|
178
|
+
}
|
|
158
179
|
},
|
|
159
180
|
memory: {
|
|
160
181
|
maxResults: 6,
|
|
161
182
|
maxTokens: 2000,
|
|
162
183
|
extraction: {
|
|
163
184
|
enabled: true,
|
|
164
|
-
async: true,
|
|
165
185
|
maxMessages: 4,
|
|
166
186
|
maxOutputTokens: 1024
|
|
167
187
|
},
|
|
@@ -173,7 +193,7 @@ const DEFAULT_AGENT_CONFIG: AgentRuntimeConfig['agent'] = {
|
|
|
173
193
|
memory: 'deepseek/deepseek-v3.2'
|
|
174
194
|
},
|
|
175
195
|
tools: {
|
|
176
|
-
maxToolSteps:
|
|
196
|
+
maxToolSteps: 200,
|
|
177
197
|
outputLimitBytes: 400_000,
|
|
178
198
|
enableBash: true,
|
|
179
199
|
enableWebSearch: true,
|
|
@@ -189,7 +209,7 @@ const DEFAULT_AGENT_CONFIG: AgentRuntimeConfig['agent'] = {
|
|
|
189
209
|
timeoutMs: 20_000
|
|
190
210
|
},
|
|
191
211
|
bash: {
|
|
192
|
-
timeoutMs:
|
|
212
|
+
timeoutMs: 600_000,
|
|
193
213
|
outputLimitBytes: 200_000
|
|
194
214
|
},
|
|
195
215
|
grepMaxFileBytes: 1_000_000,
|
|
@@ -225,7 +245,10 @@ const DEFAULT_AGENT_CONFIG: AgentRuntimeConfig['agent'] = {
|
|
|
225
245
|
enabled: true,
|
|
226
246
|
model: 'edge-tts',
|
|
227
247
|
baseUrl: '',
|
|
228
|
-
defaultVoice: 'en-US-AriaNeural'
|
|
248
|
+
defaultVoice: 'en-US-AriaNeural',
|
|
249
|
+
provider: 'edge-tts',
|
|
250
|
+
openaiModel: 'tts-1',
|
|
251
|
+
openaiVoice: 'alloy'
|
|
229
252
|
},
|
|
230
253
|
browser: {
|
|
231
254
|
enabled: true,
|
|
@@ -233,21 +256,27 @@ const DEFAULT_AGENT_CONFIG: AgentRuntimeConfig['agent'] = {
|
|
|
233
256
|
screenshotQuality: 80
|
|
234
257
|
},
|
|
235
258
|
mcp: {
|
|
236
|
-
enabled:
|
|
259
|
+
enabled: true,
|
|
237
260
|
servers: [],
|
|
238
261
|
connectionTimeoutMs: 10_000
|
|
239
262
|
},
|
|
240
263
|
reasoning: {
|
|
241
|
-
effort: '
|
|
264
|
+
effort: 'medium',
|
|
242
265
|
},
|
|
243
266
|
skills: {
|
|
244
267
|
enabled: true,
|
|
245
268
|
maxSkills: 32,
|
|
246
269
|
maxSummaryChars: 4000,
|
|
270
|
+
},
|
|
271
|
+
process: {
|
|
272
|
+
maxSessions: 16,
|
|
273
|
+
maxOutputBytes: 1_048_576,
|
|
274
|
+
defaultTimeoutMs: 1_800_000,
|
|
247
275
|
}
|
|
248
276
|
};
|
|
249
277
|
|
|
250
278
|
let cachedConfig: AgentRuntimeConfig | null = null;
|
|
279
|
+
let cachedMtime: number | null = null;
|
|
251
280
|
|
|
252
281
|
function cloneConfig<T>(value: T): T {
|
|
253
282
|
return JSON.parse(JSON.stringify(value)) as T;
|
|
@@ -257,20 +286,25 @@ function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
|
257
286
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
258
287
|
}
|
|
259
288
|
|
|
260
|
-
function mergeDefaults<T>(base: T, overrides: unknown): T {
|
|
289
|
+
function mergeDefaults<T>(base: T, overrides: unknown, pathPrefix = ''): T {
|
|
261
290
|
if (!isPlainObject(overrides)) return cloneConfig(base);
|
|
262
291
|
const result = cloneConfig(base) as Record<string, unknown>;
|
|
263
292
|
const baseObj = base as Record<string, unknown>;
|
|
264
293
|
for (const [key, value] of Object.entries(overrides)) {
|
|
265
294
|
const current = baseObj[key];
|
|
295
|
+
const fullPath = pathPrefix ? `${pathPrefix}.${key}` : key;
|
|
266
296
|
if (isPlainObject(current) && isPlainObject(value)) {
|
|
267
|
-
result[key] = mergeDefaults(current, value);
|
|
297
|
+
result[key] = mergeDefaults(current, value, fullPath);
|
|
268
298
|
continue;
|
|
269
299
|
}
|
|
270
300
|
if (Array.isArray(current) && Array.isArray(value)) {
|
|
271
301
|
result[key] = value;
|
|
272
302
|
continue;
|
|
273
303
|
}
|
|
304
|
+
if (current !== undefined && typeof value !== typeof current) {
|
|
305
|
+
console.error(`[agent-config] ${fullPath}: expected ${typeof current}, got ${typeof value}. Using default.`);
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
274
308
|
if (typeof value === typeof current) {
|
|
275
309
|
result[key] = value as unknown;
|
|
276
310
|
}
|
|
@@ -291,7 +325,17 @@ function readJson(filePath: string): unknown {
|
|
|
291
325
|
}
|
|
292
326
|
|
|
293
327
|
export function loadAgentConfig(): AgentRuntimeConfig {
|
|
294
|
-
if (cachedConfig)
|
|
328
|
+
if (cachedConfig) {
|
|
329
|
+
// Check if file has been modified since last load
|
|
330
|
+
try {
|
|
331
|
+
const stat = fs.statSync(CONFIG_PATH);
|
|
332
|
+
if (cachedMtime !== null && stat.mtimeMs === cachedMtime) {
|
|
333
|
+
return cachedConfig;
|
|
334
|
+
}
|
|
335
|
+
} catch {
|
|
336
|
+
return cachedConfig;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
295
339
|
const raw = readJson(CONFIG_PATH);
|
|
296
340
|
|
|
297
341
|
let defaultModel = DEFAULT_DEFAULT_MODEL;
|
|
@@ -326,5 +370,11 @@ export function loadAgentConfig(): AgentRuntimeConfig {
|
|
|
326
370
|
daemonHeartbeatIntervalMs,
|
|
327
371
|
agent: mergeDefaults(DEFAULT_AGENT_CONFIG, agentOverrides)
|
|
328
372
|
};
|
|
373
|
+
try {
|
|
374
|
+
const stat = fs.statSync(CONFIG_PATH);
|
|
375
|
+
cachedMtime = stat.mtimeMs;
|
|
376
|
+
} catch {
|
|
377
|
+
cachedMtime = null;
|
|
378
|
+
}
|
|
329
379
|
return cachedConfig;
|
|
330
380
|
}
|
|
@@ -36,6 +36,10 @@ export interface ContainerInput {
|
|
|
36
36
|
modelOverride?: string;
|
|
37
37
|
modelFallbacks?: string[];
|
|
38
38
|
reasoningEffort?: 'off' | 'low' | 'medium' | 'high';
|
|
39
|
+
modelCapabilities?: {
|
|
40
|
+
context_length: number;
|
|
41
|
+
max_completion_tokens?: number;
|
|
42
|
+
};
|
|
39
43
|
modelContextTokens?: number;
|
|
40
44
|
modelMaxOutputTokens?: number;
|
|
41
45
|
modelTemperature?: number;
|
|
@@ -84,6 +88,8 @@ export interface ContainerOutput {
|
|
|
84
88
|
output_truncated?: boolean;
|
|
85
89
|
}>;
|
|
86
90
|
latency_ms?: number;
|
|
91
|
+
/** Reply-to message ID parsed from agent output [[reply_to:<id>]] tags */
|
|
92
|
+
replyToId?: string;
|
|
87
93
|
/** Set by the host container-runner when stdout was truncated before parsing */
|
|
88
94
|
stdoutTruncated?: boolean;
|
|
89
95
|
}
|
|
@@ -204,11 +204,8 @@ async function processRequests(): Promise<void> {
|
|
|
204
204
|
if (!output) {
|
|
205
205
|
throw new Error('Agent worker returned no output');
|
|
206
206
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
try { fs.unlinkSync(filePath); } catch { /* request file already removed */ }
|
|
210
|
-
continue;
|
|
211
|
-
}
|
|
207
|
+
// Response completed successfully — always write it.
|
|
208
|
+
// Cancel during execution is already handled by runRequestWithCancellation().
|
|
212
209
|
const responsePath = path.join(RESPONSES_DIR, `${requestId}.json`);
|
|
213
210
|
const tmpPath = responsePath + '.tmp';
|
|
214
211
|
fs.writeFileSync(tmpPath, JSON.stringify(output));
|
|
@@ -275,6 +272,21 @@ process.on('uncaughtException', (err) => {
|
|
|
275
272
|
process.exit(1);
|
|
276
273
|
});
|
|
277
274
|
|
|
275
|
+
// --- MCP hot-reload ---
|
|
276
|
+
|
|
277
|
+
const MCP_RELOAD_FILE = '/workspace/ipc/mcp_reload';
|
|
278
|
+
|
|
279
|
+
async function checkMcpReload(): Promise<void> {
|
|
280
|
+
try {
|
|
281
|
+
if (fs.existsSync(MCP_RELOAD_FILE)) {
|
|
282
|
+
fs.unlinkSync(MCP_RELOAD_FILE);
|
|
283
|
+
log('MCP reload signal detected — agent will pick up new config on next run');
|
|
284
|
+
}
|
|
285
|
+
} catch {
|
|
286
|
+
// ignore
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
278
290
|
// --- Main loop ---
|
|
279
291
|
|
|
280
292
|
async function loop(): Promise<void> {
|
|
@@ -284,6 +296,7 @@ async function loop(): Promise<void> {
|
|
|
284
296
|
|
|
285
297
|
while (!shuttingDown) {
|
|
286
298
|
try {
|
|
299
|
+
await checkMcpReload();
|
|
287
300
|
await processRequests();
|
|
288
301
|
} catch (err) {
|
|
289
302
|
log(`Daemon loop error: ${err instanceof Error ? err.message : String(err)}`);
|