fix(wechat): pass inbound messages to goose and gate side effects at MCP tools
Memind CI / Test, build, and release guards (push) Failing after 14m19s
Memind CI / Test, build, and release guards (push) Failing after 14m19s
Stop ITL regex from intercepting new messages before Agent execution; move scheduled task and schedule write confirmation to MCP tool calls with draft commit on 确认. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { commitIntentDraft } from './intent-transaction-commit.mjs';
|
||||
import { requireUserConfirmForSideEffectTool } from './intent-tool-confirm-gate.mjs';
|
||||
|
||||
function createDraftStore() {
|
||||
let pending = null;
|
||||
return {
|
||||
async getPendingDraft(userId) {
|
||||
return pending?.userId === userId ? pending : null;
|
||||
},
|
||||
async createDraft(payload) {
|
||||
pending = {
|
||||
id: 'draft-tool-1',
|
||||
status: 'draft',
|
||||
...payload,
|
||||
};
|
||||
return pending;
|
||||
},
|
||||
async cancelDraft(id, userId) {
|
||||
if (pending?.id === id && pending.userId === userId) pending = null;
|
||||
return { id, status: 'cancelled' };
|
||||
},
|
||||
async markDraftCommitted(id, userId, committedRef) {
|
||||
pending = { ...pending, id, userId, status: 'committed', committedRef };
|
||||
return pending;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
test('requireUserConfirmForSideEffectTool creates draft instead of writing', async () => {
|
||||
const drafts = createDraftStore();
|
||||
const gate = await requireUserConfirmForSideEffectTool({
|
||||
userId: 'user-1',
|
||||
toolName: 'scheduled_task_create',
|
||||
toolArgs: {
|
||||
taskSpec: '做今日新闻页面',
|
||||
recurrence: 'daily',
|
||||
hour: 6,
|
||||
minute: 0,
|
||||
},
|
||||
intentDraftService: drafts,
|
||||
env: { H5_INTENT_TRANSACTION_ENABLED: '1' },
|
||||
});
|
||||
assert.equal(gate.proceed, false);
|
||||
assert.equal(gate.response.status, 'needs_user_confirmation');
|
||||
assert.match(gate.response.cardText, /确认/u);
|
||||
const pending = await drafts.getPendingDraft('user-1');
|
||||
assert.equal(pending.draftType, 'agent_tool');
|
||||
assert.equal(pending.payload.toolName, 'scheduled_task_create');
|
||||
});
|
||||
|
||||
test('requireUserConfirmForSideEffectTool proceeds when feature disabled', async () => {
|
||||
const gate = await requireUserConfirmForSideEffectTool({
|
||||
userId: 'user-1',
|
||||
toolName: 'scheduled_task_create',
|
||||
toolArgs: { taskSpec: '做新闻页面', hour: 6 },
|
||||
intentDraftService: createDraftStore(),
|
||||
env: { H5_INTENT_TRANSACTION_ENABLED: '0' },
|
||||
});
|
||||
assert.equal(gate.proceed, true);
|
||||
});
|
||||
|
||||
test('commitIntentDraft commits agent_tool scheduled task draft', async () => {
|
||||
const created = [];
|
||||
const committed = await commitIntentDraft({
|
||||
draft: {
|
||||
status: 'draft',
|
||||
draftType: 'agent_tool',
|
||||
title: '新闻页面',
|
||||
payload: {
|
||||
toolName: 'scheduled_task_create',
|
||||
toolArgs: {
|
||||
taskSpec: '做今日新闻页面',
|
||||
recurrence: 'daily',
|
||||
hour: 6,
|
||||
minute: 0,
|
||||
timezone: 'Asia/Shanghai',
|
||||
},
|
||||
},
|
||||
},
|
||||
userId: 'user-1',
|
||||
scheduledTaskService: {
|
||||
async createTask(input) {
|
||||
created.push(input);
|
||||
return { id: 'task-1', ...input };
|
||||
},
|
||||
},
|
||||
scheduleService: null,
|
||||
timezone: 'Asia/Shanghai',
|
||||
env: { H5_INTENT_TRANSACTION_ENABLED: '1' },
|
||||
});
|
||||
assert.equal(committed.kind, 'scheduled_task');
|
||||
assert.equal(created.length, 1);
|
||||
assert.equal(created[0].hour, 6);
|
||||
});
|
||||
Reference in New Issue
Block a user