capacitor-mobile-claw 1.2.0 → 1.3.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.
@@ -74,8 +74,13 @@ class _Headers {
74
74
  }
75
75
  get(k) { return this.#map.get(k.toLowerCase()) ?? null; }
76
76
  set(k, v) { this.#map.set(k.toLowerCase(), String(v)); }
77
+ append(k, v) {
78
+ const lk = k.toLowerCase();
79
+ const existing = this.#map.get(lk);
80
+ this.#map.set(lk, existing != null ? `${existing}, ${String(v)}` : String(v));
81
+ }
77
82
  has(k) { return this.#map.has(k.toLowerCase()); }
78
- delete(k) { this.#map.delete(k.toLowerCase()); }
83
+ delete(k) { return this.#map.delete(k.toLowerCase()); }
79
84
  forEach(cb) { this.#map.forEach((v, k) => cb(v, k, this)); }
80
85
  *entries() { yield* this.#map.entries(); }
81
86
  *keys() { yield* this.#map.keys(); }
@@ -84,13 +89,14 @@ class _Headers {
84
89
  }
85
90
 
86
91
  class _Response {
87
- #body; #bodyUsed = false; #status; #statusText; #headers; #url;
92
+ #body; #bodyUsed = false; #status; #statusText; #headers; #url; #webStream;
88
93
  constructor(body, { status = 200, statusText = '', headers = {}, url = '' } = {}) {
89
94
  this.#body = body;
90
95
  this.#status = status;
91
96
  this.#statusText = statusText;
92
97
  this.#headers = new _Headers(headers);
93
98
  this.#url = url;
99
+ this.#webStream = undefined; // lazy-init, cached
94
100
  }
95
101
  get ok() { return this.#status >= 200 && this.#status < 300; }
96
102
  get status() { return this.#status; }
@@ -99,20 +105,24 @@ class _Response {
99
105
  get url() { return this.#url; }
100
106
  get bodyUsed() { return this.#bodyUsed; }
101
107
  get body() {
108
+ if (this.#webStream !== undefined) return this.#webStream;
102
109
  if (this.#body instanceof Readable) {
103
110
  if (typeof Readable.toWeb === 'function') {
104
- return Readable.toWeb(this.#body);
111
+ this.#webStream = Readable.toWeb(this.#body);
112
+ } else {
113
+ const nodeStream = this.#body;
114
+ this.#webStream = new ReadableStream({
115
+ start(controller) {
116
+ nodeStream.on('data', (chunk) => controller.enqueue(typeof chunk === 'string' ? new TextEncoder().encode(chunk) : chunk));
117
+ nodeStream.on('end', () => controller.close());
118
+ nodeStream.on('error', (err) => controller.error(err));
119
+ },
120
+ cancel() { nodeStream.destroy(); },
121
+ });
105
122
  }
106
- const nodeStream = this.#body;
107
- return new ReadableStream({
108
- start(controller) {
109
- nodeStream.on('data', (chunk) => controller.enqueue(typeof chunk === 'string' ? new TextEncoder().encode(chunk) : chunk));
110
- nodeStream.on('end', () => controller.close());
111
- nodeStream.on('error', (err) => controller.error(err));
112
- },
113
- cancel() { nodeStream.destroy(); },
114
- });
123
+ return this.#webStream;
115
124
  }
125
+ this.#webStream = null;
116
126
  return null;
117
127
  }
118
128
  async text() {
@@ -150,9 +160,10 @@ class _Response {
150
160
  function _nativeFetch(input, init = {}) {
151
161
  return new Promise((resolve, reject) => {
152
162
  const urlStr = typeof input === 'string' ? input : (input?.url ?? String(input));
163
+ const method = (init.method || 'GET').toUpperCase();
164
+ console.error(`[DEBUG fetch] ${method} ${urlStr.slice(0,150)} body=${init.body ? init.body.length || '?' : 'none'}`);
153
165
  const parsed = new URL(urlStr);
154
166
  const transport = parsed.protocol === 'https:' ? _https : _http;
155
- const method = (init.method || 'GET').toUpperCase();
156
167
  const headers = {};
157
168
 
158
169
  if (init.headers) {
@@ -213,6 +224,7 @@ function _nativeFetch(input, init = {}) {
213
224
  headers: responseHeaders,
214
225
  url: urlStr,
215
226
  });
227
+ console.error(`[DEBUG fetch] ${method} ${urlStr.slice(0,80)} → ${res.statusCode} ${res.statusMessage || ''}`);
216
228
  resolve(response);
217
229
  });
218
230
 
@@ -230,8 +242,11 @@ function _nativeFetch(input, init = {}) {
230
242
  req.on('close', () => init.signal.removeEventListener('abort', onAbort));
231
243
  }
232
244
 
233
- req.on('error', reject);
234
- req.on('timeout', () => { req.destroy(); reject(new Error('Request timed out')); });
245
+ req.on('error', (err) => {
246
+ console.error(`[DEBUG fetch] ERROR ${method} ${urlStr.slice(0,80)}: ${err.message}`);
247
+ reject(err);
248
+ });
249
+ req.on('timeout', () => { req.destroy(); console.error(`[DEBUG fetch] TIMEOUT ${method} ${urlStr.slice(0,80)}`); reject(new Error('Request timed out')); });
235
250
 
236
251
  if (bodyData) req.write(bodyData);
237
252
  req.end();
@@ -1120,6 +1120,15 @@ function buildAgentTools() {
1120
1120
 
1121
1121
  // skillContext: if set, events are tagged as skill events and ignored by the main session view
1122
1122
  function bridgeEvent(event, skillContext = null) {
1123
+ if (event.type === 'tool_execution_start' || event.type === 'tool_execution_end') {
1124
+ console.error(`[DEBUG event] ${event.type} tool=${event.toolName} id=${event.toolCallId}`);
1125
+ } else if (event.type === 'message_update') {
1126
+ // Only log first delta to avoid spam
1127
+ const e = event.assistantMessageEvent;
1128
+ if (e.type === 'text_delta' && (e.delta||'').length > 0) {
1129
+ console.error(`[DEBUG event] text_delta len=${e.delta.length} first20=${JSON.stringify(e.delta.slice(0,20))}`);
1130
+ }
1131
+ }
1123
1132
  switch (event.type) {
1124
1133
  case 'message_update': {
1125
1134
  const e = event.assistantMessageEvent;
@@ -1550,20 +1559,27 @@ function buildSkillTools(injectedTools, milestones) {
1550
1559
  }
1551
1560
 
1552
1561
  async function runSkill(agentId, locale = 'en', injectedConfig = null) {
1553
- await refreshOAuthTokenIfNeeded(agentId);
1562
+ console.error(`[DEBUG runSkill] START agentId=${agentId} locale=${locale} hasConfig=${!!injectedConfig} configId=${injectedConfig?.id}`);
1563
+ try { await refreshOAuthTokenIfNeeded(agentId); } catch(e) { console.error(`[DEBUG runSkill] OAuth refresh err: ${e.message}`); }
1554
1564
  const authProfiles = loadAuthProfiles(agentId);
1565
+ const profileKeys = Object.keys(authProfiles.profiles || {});
1566
+ console.error(`[DEBUG runSkill] Auth: ${profileKeys.length} profiles [${profileKeys.join(',')}] lastGood=${JSON.stringify(authProfiles.lastGood)}`);
1555
1567
  const apiKey = resolveApiKey(authProfiles);
1556
1568
 
1557
1569
  if (!apiKey) {
1570
+ console.error(`[DEBUG runSkill] NO API KEY — aborting`);
1558
1571
  channel.send('message', {
1559
1572
  type: 'agent.error',
1560
1573
  error: 'No AI provider configured. Connect a provider first.',
1561
1574
  });
1562
1575
  return;
1563
1576
  }
1577
+ console.error(`[DEBUG runSkill] API key: type=${apiKey.startsWith('sk-ant-oat') ? 'oauth' : 'api_key'} len=${apiKey.length} prefix=${apiKey.slice(0,15)}...`);
1564
1578
 
1565
1579
  const modelId = 'claude-sonnet-4-5';
1580
+ console.error(`[DEBUG runSkill] Getting model anthropic/${modelId}`);
1566
1581
  const model = getModel('anthropic', modelId);
1582
+ console.error(`[DEBUG runSkill] Model: ${JSON.stringify({id: model?.id, provider: model?.provider, api: model?.api}).slice(0,200)}`);
1567
1583
 
1568
1584
  // Use injected config from client if available, fall back to hardcoded defaults
1569
1585
  const milestones = injectedConfig?.milestones || SETUP_MILESTONES;
@@ -1609,12 +1625,27 @@ async function runSkill(agentId, locale = 'en', injectedConfig = null) {
1609
1625
  });
1610
1626
 
1611
1627
  // Notify client that the skill session has started (used to init isolated conversation view)
1628
+ console.error(`[DEBUG runSkill] Sending skill.session_started skillId=${skillId} sessionKey=${sessionKey}`);
1612
1629
  channel.send('message', { type: 'skill.session_started', sessionKey, skillId });
1613
1630
 
1614
1631
  const startTime = Date.now();
1632
+ console.error(`[DEBUG runSkill] Calling agent.prompt() with kickoff (${kickoff.length} chars): ${kickoff.slice(0,80)}`);
1615
1633
  try {
1616
1634
  await agent.prompt(kickoff);
1635
+ console.error(`[DEBUG runSkill] agent.prompt() resolved, waitForIdle()...`);
1617
1636
  await agent.waitForIdle();
1637
+ console.error(`[DEBUG runSkill] Idle. Duration=${Date.now() - startTime}ms msgs=${agent.state.messages.length}`);
1638
+ // Check if the agent silently errored (prompt() resolves even on stream errors)
1639
+ if (agent.state.error) {
1640
+ console.error(`[DEBUG runSkill] AGENT STATE ERROR: ${agent.state.error}`);
1641
+ }
1642
+ const lastMsg = agent.state.messages[agent.state.messages.length - 1];
1643
+ if (lastMsg) {
1644
+ console.error(`[DEBUG runSkill] Last msg: role=${lastMsg.role} stopReason=${lastMsg.stopReason} errorMessage=${lastMsg.errorMessage || 'none'}`);
1645
+ if (lastMsg.errorMessage) {
1646
+ console.error(`[DEBUG runSkill] ERROR DETAIL: ${lastMsg.errorMessage}`);
1647
+ }
1648
+ }
1618
1649
 
1619
1650
  saveSession(agent, agentId, sessionKey, startTime);
1620
1651
 
@@ -1628,7 +1659,11 @@ async function runSkill(agentId, locale = 'en', injectedConfig = null) {
1628
1659
  durationMs: Date.now() - startTime,
1629
1660
  });
1630
1661
  channel.send('message', { type: 'skill.ended', skillId, sessionKey });
1662
+ console.error(`[DEBUG runSkill] skill.ended sent (success)`);
1631
1663
  } catch (err) {
1664
+ console.error(`[DEBUG runSkill] CAUGHT ERROR: ${err.message}`);
1665
+ console.error(`[DEBUG runSkill] Error details: name=${err.name} status=${err.status} code=${err.code}`);
1666
+ console.error(err.stack || '(no stack)');
1632
1667
  channel.send('message', {
1633
1668
  type: 'agent.error',
1634
1669
  skill: sessionKey,
@@ -1750,9 +1785,12 @@ async function runAgentLoop(agentId, sessionKey, prompt, requestedModel) {
1750
1785
 
1751
1786
  // Run
1752
1787
  const startTime = Date.now();
1788
+ console.error(`[DEBUG] Calling agent.prompt()...`);
1753
1789
  try {
1754
1790
  await agent.prompt(prompt);
1791
+ console.error(`[DEBUG] agent.prompt() resolved, calling waitForIdle()...`);
1755
1792
  await agent.waitForIdle();
1793
+ console.error(`[DEBUG] agent idle. Duration=${Date.now() - startTime}ms messages=${agent.state.messages.length}`);
1756
1794
 
1757
1795
  // Save session + send completion
1758
1796
  currentSessionKey = sessionKey;
@@ -1766,9 +1804,13 @@ async function runAgentLoop(agentId, sessionKey, prompt, requestedModel) {
1766
1804
  cumulativeUsage: usage,
1767
1805
  durationMs: Date.now() - startTime,
1768
1806
  });
1807
+ console.error(`[DEBUG] agent.completed sent. tokens=${JSON.stringify(usage)}`);
1769
1808
  // Keep currentAgent alive for multi-turn follow-ups
1770
1809
  currentAbortController = null;
1771
1810
  } catch (err) {
1811
+ console.error(`[DEBUG] agent.prompt THREW: ${err.message}`);
1812
+ console.error(`[DEBUG] Error details: status=${err.status} code=${err.code} name=${err.name}`);
1813
+ console.error(err.stack || '(no stack)');
1772
1814
  const retryable = isTransientError(err);
1773
1815
  channel.send('message', {
1774
1816
  type: 'agent.error',
@@ -1954,9 +1996,11 @@ channel.addListener('message', async (event) => {
1954
1996
  }
1955
1997
 
1956
1998
  case 'skill.start': {
1999
+ console.error(`[DEBUG] skill.start: skill=${msg.skill} agentId=${msg.agentId} locale=${msg.locale}`);
1957
2000
  const agentId = msg.agentId || 'main';
1958
2001
  const locale = msg.locale || 'en';
1959
2002
  await runSkill(agentId, locale, msg.config || null);
2003
+ console.error(`[DEBUG] skill.start completed`);
1960
2004
  break;
1961
2005
  }
1962
2006
 
@@ -2372,7 +2416,7 @@ channel.addListener('message', async (event) => {
2372
2416
  }
2373
2417
 
2374
2418
  default:
2375
- console.log('[Worker] Unknown message type:', msg.type);
2419
+ console.error(`[DEBUG] Unknown message type: ${msg.type}`);
2376
2420
  }
2377
2421
  });
2378
2422
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "capacitor-mobile-claw",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
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",