@myvillage/cli 1.30.0 → 1.31.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/package.json +1 -1
- package/src/agent-runtime/loop.js +42 -4
- package/src/utils/api.js +1 -1
package/package.json
CHANGED
|
@@ -12,7 +12,8 @@ import { getMCPTools, cleanupMCPClients } from './mcp-client.js';
|
|
|
12
12
|
import { gatherContext } from './context.js';
|
|
13
13
|
import { isWithinActiveHours, getNextCheckInMs } from './scheduler.js';
|
|
14
14
|
import { parse as parseYaml } from 'yaml';
|
|
15
|
-
import { postAgentHeartbeat, listAgentTasks, claimAgentTask, completeAgentTask } from '../utils/api.js';
|
|
15
|
+
import { postAgentHeartbeat, listAgentTasks, claimAgentTask, completeAgentTask, refreshAccessToken } from '../utils/api.js';
|
|
16
|
+
import { getAccessToken, isTokenExpired } from '../utils/auth.js';
|
|
16
17
|
import { readAgentWisdom } from '../utils/wisdom.js';
|
|
17
18
|
|
|
18
19
|
export async function agentLoop(agentName, { signal }) {
|
|
@@ -115,6 +116,29 @@ export async function agentLoop(agentName, { signal }) {
|
|
|
115
116
|
logActivity(agentDir, { type: 'loop_start', iteration });
|
|
116
117
|
updateHeartbeat(agentDir);
|
|
117
118
|
|
|
119
|
+
// Refresh OAuth token and reconnect MCP if the access token rotated.
|
|
120
|
+
// Why: the daemon is started with a snapshot of MYVILLAGE_ACCESS_TOKEN in
|
|
121
|
+
// env (see agent-local.js). The MCP client baked that token into its
|
|
122
|
+
// Authorization header at construction time and has no refresh path of
|
|
123
|
+
// its own — so once the OAuth token expires (~1h) every MCP tool call
|
|
124
|
+
// 401s until the daemon is restarted. Re-read credentials each loop and
|
|
125
|
+
// swap the MCP client when the token changes.
|
|
126
|
+
try {
|
|
127
|
+
if (isTokenExpired()) {
|
|
128
|
+
await refreshAccessToken();
|
|
129
|
+
}
|
|
130
|
+
const currentToken = getAccessToken();
|
|
131
|
+
if (currentToken && currentToken !== process.env.MYVILLAGE_ACCESS_TOKEN) {
|
|
132
|
+
process.env.MYVILLAGE_ACCESS_TOKEN = currentToken;
|
|
133
|
+
await cleanupMCPClients();
|
|
134
|
+
const refreshed = await getMCPTools(agentDir, config);
|
|
135
|
+
tools = refreshed.tools;
|
|
136
|
+
logActivity(agentDir, { type: 'mcp_reconnected', reason: 'token rotated' });
|
|
137
|
+
}
|
|
138
|
+
} catch (err) {
|
|
139
|
+
logActivity(agentDir, { type: 'error', error: `Token refresh / MCP reconnect failed: ${err.message}` });
|
|
140
|
+
}
|
|
141
|
+
|
|
118
142
|
// Activity counters for this iteration
|
|
119
143
|
const activity = {
|
|
120
144
|
postsCreated: 0,
|
|
@@ -335,16 +359,30 @@ Guidelines:
|
|
|
335
359
|
// whether the action tools actually succeeded — not on the model's
|
|
336
360
|
// self-report. The LLM sometimes claims "I posted!" after a tool error.
|
|
337
361
|
if (activeTask && config.man?.village_agent_id) {
|
|
362
|
+
// Three independent failure signals:
|
|
363
|
+
// 1. action tools tried and all failed (original signal — still right)
|
|
364
|
+
// 2. any tool errored AND no action tool succeeded (e.g. an
|
|
365
|
+
// MCP/auth error during discovery means the LLM never got to
|
|
366
|
+
// call an action tool — older versions marked these COMPLETED)
|
|
367
|
+
// 3. the LLM explicitly declared failure in its final text
|
|
368
|
+
// (matches the `**Task Failed:` sentinel the model emits when
|
|
369
|
+
// it gives up after repeated tool errors)
|
|
370
|
+
const llmDeclaredFailure = /^\s*\*\*Task Failed/i.test(result.text || '');
|
|
371
|
+
const hasToolErrors = taskActionAudit.toolErrors.length > 0;
|
|
338
372
|
const shouldFail =
|
|
339
|
-
taskActionAudit.actionToolsCalled > 0 &&
|
|
340
|
-
taskActionAudit.actionToolsSucceeded === 0
|
|
373
|
+
(taskActionAudit.actionToolsCalled > 0 && taskActionAudit.actionToolsSucceeded === 0) ||
|
|
374
|
+
(hasToolErrors && taskActionAudit.actionToolsSucceeded === 0) ||
|
|
375
|
+
llmDeclaredFailure;
|
|
341
376
|
|
|
342
377
|
try {
|
|
343
378
|
if (shouldFail) {
|
|
344
379
|
const firstError = taskActionAudit.toolErrors[0];
|
|
380
|
+
const firstLine = (result.text || '').split('\n').find(l => l.trim()) || '';
|
|
345
381
|
const errorMessage = firstError
|
|
346
382
|
? `${firstError.tool} failed: ${firstError.message}`
|
|
347
|
-
:
|
|
383
|
+
: llmDeclaredFailure
|
|
384
|
+
? firstLine.replace(/^\*\*|\*\*$/g, '').slice(0, 300) || 'Agent declared task failed'
|
|
385
|
+
: 'Action tools called but all failed';
|
|
348
386
|
await completeAgentTask(config.man.village_agent_id, activeTask.id, {
|
|
349
387
|
errorMessage,
|
|
350
388
|
output: {
|