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
- function bridgeEvent(event) {
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
- base.execute = async (_id, params) => {
1463
- channel.send('message', { type: eventType, ...params });
1464
- return toToolResult({ success: true, applied: true });
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 runSetupSkill(agentId, locale = 'en', injectedConfig = null) {
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 sessionKey = `setup/${Date.now()}`;
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
- error: err.message || 'Setup skill error',
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 runSetupSkill(agentId, locale, msg.config || null);
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.10",
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",