capacitor-mobile-claw 1.0.10 → 1.0.12
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.
|
@@ -768,6 +768,37 @@ async function gitDiffTool(args) {
|
|
|
768
768
|
const pendingPreExecuteHooks = new Map();
|
|
769
769
|
const PRE_EXECUTE_TTL_MS = 120_000; // Auto-cancel after 2 minutes
|
|
770
770
|
|
|
771
|
+
// Map of requestId → resolver callback for skill bridge tools that wait for client result
|
|
772
|
+
const pendingSkillToolResults = new Map();
|
|
773
|
+
const SKILL_TOOL_TTL_MS = 300_000; // 5 minutes — user may need time to complete OAuth
|
|
774
|
+
|
|
775
|
+
function waitForSkillToolResult(requestId, eventType, signal) {
|
|
776
|
+
return new Promise((resolve) => {
|
|
777
|
+
const ttlTimer = setTimeout(() => {
|
|
778
|
+
if (pendingSkillToolResults.has(requestId)) {
|
|
779
|
+
pendingSkillToolResults.delete(requestId);
|
|
780
|
+
resolve(null);
|
|
781
|
+
}
|
|
782
|
+
}, SKILL_TOOL_TTL_MS);
|
|
783
|
+
|
|
784
|
+
pendingSkillToolResults.set(requestId, (result) => {
|
|
785
|
+
clearTimeout(ttlTimer);
|
|
786
|
+
resolve(result);
|
|
787
|
+
});
|
|
788
|
+
|
|
789
|
+
if (signal) {
|
|
790
|
+
const onAbort = () => {
|
|
791
|
+
clearTimeout(ttlTimer);
|
|
792
|
+
if (pendingSkillToolResults.has(requestId)) {
|
|
793
|
+
pendingSkillToolResults.delete(requestId);
|
|
794
|
+
resolve(null);
|
|
795
|
+
}
|
|
796
|
+
};
|
|
797
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
798
|
+
}
|
|
799
|
+
});
|
|
800
|
+
}
|
|
801
|
+
|
|
771
802
|
function waitForPreExecuteResult(toolCallId, toolName, signal) {
|
|
772
803
|
return new Promise((resolve) => {
|
|
773
804
|
const ttlTimer = setTimeout(() => {
|
|
@@ -1057,15 +1088,16 @@ function buildAgentTools() {
|
|
|
1057
1088
|
|
|
1058
1089
|
// ── AgentEvent → Bridge event mapping ────────────────────────────────────
|
|
1059
1090
|
|
|
1060
|
-
|
|
1091
|
+
// skillContext: if set, events are tagged as skill events and ignored by the main session view
|
|
1092
|
+
function bridgeEvent(event, skillContext = null) {
|
|
1061
1093
|
switch (event.type) {
|
|
1062
1094
|
case 'message_update': {
|
|
1063
1095
|
const e = event.assistantMessageEvent;
|
|
1064
1096
|
if (e.type === 'text_delta') {
|
|
1065
|
-
channel.send('message', { type: 'agent.event', eventType: 'text_delta', data: { text: e.delta } });
|
|
1097
|
+
channel.send('message', { type: 'agent.event', eventType: 'text_delta', data: { text: e.delta }, skill: skillContext });
|
|
1066
1098
|
}
|
|
1067
1099
|
if (e.type === 'thinking_delta') {
|
|
1068
|
-
channel.send('message', { type: 'agent.event', eventType: 'thinking', data: { text: e.delta } });
|
|
1100
|
+
channel.send('message', { type: 'agent.event', eventType: 'thinking', data: { text: e.delta }, skill: skillContext });
|
|
1069
1101
|
}
|
|
1070
1102
|
break;
|
|
1071
1103
|
}
|
|
@@ -1074,6 +1106,7 @@ function bridgeEvent(event) {
|
|
|
1074
1106
|
type: 'agent.event',
|
|
1075
1107
|
eventType: 'tool_use',
|
|
1076
1108
|
data: { toolName: event.toolName, toolCallId: event.toolCallId, args: event.args },
|
|
1109
|
+
skill: skillContext,
|
|
1077
1110
|
});
|
|
1078
1111
|
break;
|
|
1079
1112
|
case 'tool_execution_end':
|
|
@@ -1081,6 +1114,7 @@ function bridgeEvent(event) {
|
|
|
1081
1114
|
type: 'agent.event',
|
|
1082
1115
|
eventType: 'tool_result',
|
|
1083
1116
|
data: { toolName: event.toolName, toolCallId: event.toolCallId, result: event.result },
|
|
1117
|
+
skill: skillContext,
|
|
1084
1118
|
});
|
|
1085
1119
|
break;
|
|
1086
1120
|
}
|
|
@@ -1456,12 +1490,25 @@ function buildSkillTools(injectedTools, milestones) {
|
|
|
1456
1490
|
return base;
|
|
1457
1491
|
}
|
|
1458
1492
|
|
|
1459
|
-
// bridgeEvent tools — emit tool input as a bridge message to the UI
|
|
1493
|
+
// bridgeEvent tools — emit tool input as a bridge message to the UI.
|
|
1494
|
+
// If waitForResult is true, the tool suspends until the client sends
|
|
1495
|
+
// a skill.tool_result message (same pattern as pendingPreExecuteHooks).
|
|
1460
1496
|
if (toolDef.bridgeEvent) {
|
|
1461
1497
|
const eventType = toolDef.bridgeEvent;
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1498
|
+
const waitForResult = !!toolDef.waitForResult;
|
|
1499
|
+
base.execute = async (_id, params, signal) => {
|
|
1500
|
+
if (!waitForResult) {
|
|
1501
|
+
channel.send('message', { type: eventType, ...params });
|
|
1502
|
+
return toToolResult({ success: true, applied: true });
|
|
1503
|
+
}
|
|
1504
|
+
// Send bridge event then suspend until client responds
|
|
1505
|
+
const requestId = `${eventType}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
1506
|
+
channel.send('message', { type: eventType, requestId, ...params });
|
|
1507
|
+
const result = await waitForSkillToolResult(requestId, eventType, signal);
|
|
1508
|
+
if (!result) {
|
|
1509
|
+
return toToolResult({ success: false, error: 'Request timed out or was cancelled.' });
|
|
1510
|
+
}
|
|
1511
|
+
return toToolResult(result);
|
|
1465
1512
|
};
|
|
1466
1513
|
return base;
|
|
1467
1514
|
}
|
|
@@ -1472,7 +1519,7 @@ function buildSkillTools(injectedTools, milestones) {
|
|
|
1472
1519
|
});
|
|
1473
1520
|
}
|
|
1474
1521
|
|
|
1475
|
-
async function
|
|
1522
|
+
async function runSkill(agentId, locale = 'en', injectedConfig = null) {
|
|
1476
1523
|
await refreshOAuthTokenIfNeeded(agentId);
|
|
1477
1524
|
const authProfiles = loadAuthProfiles(agentId);
|
|
1478
1525
|
const apiKey = resolveApiKey(authProfiles);
|
|
@@ -1505,7 +1552,8 @@ async function runSetupSkill(agentId, locale = 'en', injectedConfig = null) {
|
|
|
1505
1552
|
const filteredMcp = mcpTools.filter(t => !setupNames.has(t.name));
|
|
1506
1553
|
const tools = [...setupTools, ...filteredBase, ...filteredMcp];
|
|
1507
1554
|
|
|
1508
|
-
const
|
|
1555
|
+
const skillId = injectedConfig?.id || 'setup';
|
|
1556
|
+
const sessionKey = `${skillId}/${Date.now()}`;
|
|
1509
1557
|
const agent = new Agent({
|
|
1510
1558
|
initialState: {
|
|
1511
1559
|
systemPrompt,
|
|
@@ -1524,12 +1572,15 @@ async function runSetupSkill(agentId, locale = 'en', injectedConfig = null) {
|
|
|
1524
1572
|
setupMilestonesReached.clear();
|
|
1525
1573
|
|
|
1526
1574
|
agent.subscribe((event) => {
|
|
1527
|
-
bridgeEvent(event);
|
|
1575
|
+
bridgeEvent(event, sessionKey); // tag all events with skill sessionKey
|
|
1528
1576
|
if (event.type === 'tool_execution_end' && sessionKey) {
|
|
1529
1577
|
_checkpointMessages(agent, agentId, sessionKey);
|
|
1530
1578
|
}
|
|
1531
1579
|
});
|
|
1532
1580
|
|
|
1581
|
+
// Notify client that the skill session has started (used to init isolated conversation view)
|
|
1582
|
+
channel.send('message', { type: 'skill.session_started', sessionKey, skillId });
|
|
1583
|
+
|
|
1533
1584
|
const startTime = Date.now();
|
|
1534
1585
|
try {
|
|
1535
1586
|
await agent.prompt(kickoff);
|
|
@@ -1541,17 +1592,21 @@ async function runSetupSkill(agentId, locale = 'en', injectedConfig = null) {
|
|
|
1541
1592
|
channel.send('message', {
|
|
1542
1593
|
type: 'agent.completed',
|
|
1543
1594
|
sessionKey,
|
|
1595
|
+
skill: sessionKey,
|
|
1544
1596
|
usage,
|
|
1545
1597
|
cumulativeUsage: usage,
|
|
1546
1598
|
durationMs: Date.now() - startTime,
|
|
1547
1599
|
});
|
|
1600
|
+
channel.send('message', { type: 'skill.ended', skillId, sessionKey });
|
|
1548
1601
|
} catch (err) {
|
|
1549
1602
|
channel.send('message', {
|
|
1550
1603
|
type: 'agent.error',
|
|
1551
|
-
|
|
1604
|
+
skill: sessionKey,
|
|
1605
|
+
error: err.message || 'Skill error',
|
|
1552
1606
|
code: err.status ? String(err.status) : undefined,
|
|
1553
1607
|
retryable: isTransientError(err),
|
|
1554
1608
|
});
|
|
1609
|
+
channel.send('message', { type: 'skill.ended', skillId, sessionKey });
|
|
1555
1610
|
}
|
|
1556
1611
|
}
|
|
1557
1612
|
|
|
@@ -1833,6 +1888,16 @@ channel.addListener('message', async (event) => {
|
|
|
1833
1888
|
break;
|
|
1834
1889
|
}
|
|
1835
1890
|
|
|
1891
|
+
case 'skill.tool_result': {
|
|
1892
|
+
// Client resolved a waiting bridgeEvent tool (e.g. accounts_start_oauth after user taps "Connect")
|
|
1893
|
+
const resolver = pendingSkillToolResults.get(msg.requestId);
|
|
1894
|
+
if (resolver) {
|
|
1895
|
+
pendingSkillToolResults.delete(msg.requestId);
|
|
1896
|
+
resolver(msg.result);
|
|
1897
|
+
}
|
|
1898
|
+
break;
|
|
1899
|
+
}
|
|
1900
|
+
|
|
1836
1901
|
case 'agent.steer': {
|
|
1837
1902
|
if (currentAgent) {
|
|
1838
1903
|
currentAgent.steer({ role: 'user', content: msg.text, timestamp: Date.now() });
|
|
@@ -1843,7 +1908,7 @@ channel.addListener('message', async (event) => {
|
|
|
1843
1908
|
case 'skill.start': {
|
|
1844
1909
|
const agentId = msg.agentId || 'main';
|
|
1845
1910
|
const locale = msg.locale || 'en';
|
|
1846
|
-
await
|
|
1911
|
+
await runSkill(agentId, locale, msg.config || null);
|
|
1847
1912
|
break;
|
|
1848
1913
|
}
|
|
1849
1914
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "capacitor-mobile-claw",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.12",
|
|
4
4
|
"description": "On-device AI agent engine for Capacitor apps — embedded Node.js worker with LLM, file tools, code execution, git, and extensible MCP server",
|
|
5
5
|
"main": "dist/esm/index.js",
|
|
6
6
|
"types": "dist/esm/index.d.ts",
|