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
|
-
|
|
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
|
-
|
|
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',
|
|
234
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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",
|