doer-agent 0.6.9 → 0.7.1
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-codex-app-rpc.js +58 -1
- package/dist/mobile-mcp-server.js +53 -0
- package/package.json +1 -1
|
@@ -1,5 +1,62 @@
|
|
|
1
1
|
import { StringCodec } from "nats";
|
|
2
2
|
const codexAppRpcCodec = StringCodec();
|
|
3
|
+
const codexAppRpcOmitRules = [
|
|
4
|
+
{
|
|
5
|
+
method: "thread/turns/list",
|
|
6
|
+
itemType: "imageGeneration",
|
|
7
|
+
keys: ["result"],
|
|
8
|
+
},
|
|
9
|
+
];
|
|
10
|
+
function recordValue(value) {
|
|
11
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
12
|
+
}
|
|
13
|
+
function omitKeysFromRecord(record, keys) {
|
|
14
|
+
const next = { ...record };
|
|
15
|
+
for (const key of keys) {
|
|
16
|
+
delete next[key];
|
|
17
|
+
}
|
|
18
|
+
return next;
|
|
19
|
+
}
|
|
20
|
+
function omitRulesForThreadTurnItem(method, item) {
|
|
21
|
+
return codexAppRpcOmitRules.filter((rule) => {
|
|
22
|
+
if (rule.method !== method) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
return !rule.itemType || item.type === rule.itemType;
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
function applyCodexAppRpcOmitRules(method, value) {
|
|
29
|
+
if (!codexAppRpcOmitRules.some((rule) => rule.method === method)) {
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
const response = recordValue(value);
|
|
33
|
+
if (!response || !Array.isArray(response.data)) {
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
...response,
|
|
38
|
+
data: response.data.map((turnValue) => {
|
|
39
|
+
const turn = recordValue(turnValue);
|
|
40
|
+
if (!turn || !Array.isArray(turn.items)) {
|
|
41
|
+
return turnValue;
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
...turn,
|
|
45
|
+
items: turn.items.map((itemValue) => {
|
|
46
|
+
const item = recordValue(itemValue);
|
|
47
|
+
if (!item) {
|
|
48
|
+
return itemValue;
|
|
49
|
+
}
|
|
50
|
+
const rules = omitRulesForThreadTurnItem(method, item);
|
|
51
|
+
if (rules.length === 0) {
|
|
52
|
+
return itemValue;
|
|
53
|
+
}
|
|
54
|
+
return rules.reduce((current, rule) => omitKeysFromRecord(current, rule.keys), item);
|
|
55
|
+
}),
|
|
56
|
+
};
|
|
57
|
+
}),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
3
60
|
function normalizeCodexAppRpcRequest(args) {
|
|
4
61
|
const requestId = typeof args.request.requestId === "string" ? args.request.requestId.trim() : "";
|
|
5
62
|
const requestAgentId = typeof args.request.agentId === "string" ? args.request.agentId.trim() : "";
|
|
@@ -21,7 +78,7 @@ async function handleCodexAppRpcMessage(args) {
|
|
|
21
78
|
const payload = JSON.parse(codexAppRpcCodec.decode(args.msg.data));
|
|
22
79
|
const request = normalizeCodexAppRpcRequest({ request: payload, agentId: args.agentId });
|
|
23
80
|
requestId = request.requestId;
|
|
24
|
-
const result = await args.manager.request(request.method, request.params);
|
|
81
|
+
const result = applyCodexAppRpcOmitRules(request.method, await args.manager.request(request.method, request.params));
|
|
25
82
|
args.msg.respond(codexAppRpcCodec.encode(JSON.stringify({
|
|
26
83
|
requestId,
|
|
27
84
|
ok: true,
|
|
@@ -100,6 +100,18 @@ async function showMobileNotification(args) {
|
|
|
100
100
|
url: args.url,
|
|
101
101
|
});
|
|
102
102
|
}
|
|
103
|
+
async function showMobileConfirmation(args) {
|
|
104
|
+
const config = getConfig();
|
|
105
|
+
const resolvedDeviceId = await resolveDeviceId(args.deviceId);
|
|
106
|
+
return await postJson(`/api/users/${encodeURIComponent(config.userId)}/agents/${encodeURIComponent(config.agentId)}/mobile-agents/${encodeURIComponent(resolvedDeviceId)}/show-confirmation`, {
|
|
107
|
+
title: args.title,
|
|
108
|
+
text: args.text,
|
|
109
|
+
requestId: args.requestId,
|
|
110
|
+
yesLabel: args.yesLabel,
|
|
111
|
+
noLabel: args.noLabel,
|
|
112
|
+
notificationId: args.notificationId,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
103
115
|
async function getMobileLogs(args) {
|
|
104
116
|
const config = getConfig();
|
|
105
117
|
const resolvedDeviceId = await resolveDeviceId(args.deviceId);
|
|
@@ -167,6 +179,9 @@ function extractLocationEvents(events) {
|
|
|
167
179
|
function extractNotifications(events) {
|
|
168
180
|
return events.filter((event) => event.kind.startsWith("notification."));
|
|
169
181
|
}
|
|
182
|
+
function extractConfirmations(events) {
|
|
183
|
+
return events.filter((event) => event.kind.startsWith("confirmation."));
|
|
184
|
+
}
|
|
170
185
|
async function main() {
|
|
171
186
|
const server = new McpServer({
|
|
172
187
|
name: "doer-mobile",
|
|
@@ -294,6 +309,44 @@ async function main() {
|
|
|
294
309
|
structuredContent: result,
|
|
295
310
|
};
|
|
296
311
|
});
|
|
312
|
+
server.registerTool("mobile_show_confirmation", {
|
|
313
|
+
description: "Show a high-priority mobile confirmation notification with only Yes/No actions. Responses are stored in the mobile event log as confirmation.response.",
|
|
314
|
+
inputSchema: {
|
|
315
|
+
deviceId: z.string().optional().describe("Mobile device id. Defaults to the first registered mobile agent."),
|
|
316
|
+
requestId: z.string().optional().describe("Caller-provided id for matching the eventual confirmation.response log. Generated by the server when omitted."),
|
|
317
|
+
title: z.string().min(1).describe("Confirmation title."),
|
|
318
|
+
text: z.string().min(1).describe("Confirmation body text."),
|
|
319
|
+
yesLabel: z.string().optional().describe("Label for the affirmative action. Defaults to Yes."),
|
|
320
|
+
noLabel: z.string().optional().describe("Label for the negative action. Defaults to No."),
|
|
321
|
+
notificationId: z.number().int().optional().describe("Optional Android notification id. Reusing an id updates the existing confirmation notification."),
|
|
322
|
+
},
|
|
323
|
+
}, async ({ deviceId, requestId, title, text, yesLabel, noLabel, notificationId }) => {
|
|
324
|
+
const result = await showMobileConfirmation({ deviceId, requestId, title, text, yesLabel, noLabel, notificationId });
|
|
325
|
+
return {
|
|
326
|
+
content: [{ type: "text", text: formatJson(result) }],
|
|
327
|
+
structuredContent: result,
|
|
328
|
+
};
|
|
329
|
+
});
|
|
330
|
+
server.registerTool("mobile_search_confirmation_logs", {
|
|
331
|
+
description: "Return recent confirmation request/response/dismissed events stored in mobile logs.",
|
|
332
|
+
inputSchema: {
|
|
333
|
+
deviceId: z.string().optional().describe("Mobile device id. Defaults to the first registered mobile agent."),
|
|
334
|
+
limit: z.number().int().min(1).max(1000).optional().describe("How many recent events to scan."),
|
|
335
|
+
requestId: z.string().optional().describe("Optional confirmation request id to search for."),
|
|
336
|
+
},
|
|
337
|
+
}, async ({ deviceId, limit, requestId }) => {
|
|
338
|
+
const page = await searchMobileLogs({
|
|
339
|
+
deviceId,
|
|
340
|
+
kind: "confirmation.",
|
|
341
|
+
limit: limit ?? 200,
|
|
342
|
+
query: requestId?.trim() || undefined,
|
|
343
|
+
});
|
|
344
|
+
const events = extractConfirmations(page.events);
|
|
345
|
+
return {
|
|
346
|
+
content: [{ type: "text", text: formatJson({ events }) }],
|
|
347
|
+
structuredContent: { events },
|
|
348
|
+
};
|
|
349
|
+
});
|
|
297
350
|
server.registerTool("mobile_search_notification_logs", {
|
|
298
351
|
description: "Return recent notification events stored in mobile logs.",
|
|
299
352
|
inputSchema: {
|