anywhere-ai 0.0.26 → 0.0.27

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/cli.js CHANGED
@@ -12,11 +12,11 @@ import qrcode from "qrcode";
12
12
  var args = process.argv.slice(2);
13
13
  var command = args.find((a) => !a.startsWith("-"));
14
14
  if (args.includes("--version") || args.includes("-v")) {
15
- console.log(`anywhere-ai v${"0.0.26"}`);
15
+ console.log(`anywhere-ai v${"0.0.27"}`);
16
16
  process.exit(0);
17
17
  }
18
18
  if (args.includes("--help") || args.includes("-h") || command === "help") {
19
- console.log(`anywhere-ai v${"0.0.26"} \u2014 Mobile coding agent
19
+ console.log(`anywhere-ai v${"0.0.27"} \u2014 Mobile coding agent
20
20
 
21
21
  Usage: anywhere-ai [command] [options]
22
22
 
package/dist/server.js CHANGED
@@ -67,6 +67,8 @@ async function getCwdFromJSONL(sessionId) {
67
67
  }
68
68
  var activeSessions = /* @__PURE__ */ new Set();
69
69
  var pendingPermissions = /* @__PURE__ */ new Map();
70
+ var permissionToolUseIDs = /* @__PURE__ */ new Map();
71
+ var permissionInputs = /* @__PURE__ */ new Map();
70
72
  function getAssistantText(msg) {
71
73
  if (msg.type !== "assistant") return null;
72
74
  return msg.message.content.filter((block) => block.type === "text").map((block) => block.text).join("");
@@ -83,22 +85,33 @@ var permReqCounter = 0;
83
85
  function makeCanUseTool(sessionHint, permissionMode) {
84
86
  return async (toolName, input, options) => {
85
87
  const requestId = `perm_${++permReqCounter}_${Date.now()}`;
88
+ console.log(`[perm] canUseTool: ${toolName} requestId=${requestId}`);
86
89
  const req = {
87
90
  requestId,
88
91
  toolName,
89
92
  input,
90
93
  description: input.description,
91
94
  decisionReason: options.decisionReason,
92
- suggestions: options.suggestions
95
+ suggestions: options.suggestions,
96
+ toolUseID: options.toolUseID
93
97
  };
94
98
  const cb = permissionCallbacks.get(sessionHint);
95
- if (cb) cb(req);
99
+ if (cb) {
100
+ cb(req);
101
+ } else {
102
+ console.log(`[perm] WARNING: no callback for session=${sessionHint}, requestId=${requestId}`);
103
+ }
104
+ if (options.toolUseID) permissionToolUseIDs.set(requestId, options.toolUseID);
105
+ permissionInputs.set(requestId, input);
96
106
  return new Promise((resolve) => {
97
107
  pendingPermissions.set(requestId, resolve);
98
108
  const timeout = setTimeout(
99
109
  () => {
100
110
  if (pendingPermissions.has(requestId)) {
101
111
  pendingPermissions.delete(requestId);
112
+ permissionToolUseIDs.delete(requestId);
113
+ permissionInputs.delete(requestId);
114
+ console.log(`[perm] TIMEOUT: ${requestId}`);
102
115
  resolve({
103
116
  behavior: "deny",
104
117
  message: "Permission request timed out"
@@ -111,13 +124,18 @@ function makeCanUseTool(sessionHint, permissionMode) {
111
124
  clearTimeout(timeout);
112
125
  if (pendingPermissions.has(requestId)) {
113
126
  pendingPermissions.delete(requestId);
127
+ permissionToolUseIDs.delete(requestId);
128
+ console.log(`[perm] ABORT: ${requestId}`);
114
129
  resolve({ behavior: "deny", message: "Request aborted" });
130
+ } else {
115
131
  }
116
132
  });
117
133
  const origResolve = resolve;
118
134
  pendingPermissions.set(requestId, (result) => {
119
135
  clearTimeout(timeout);
120
136
  pendingPermissions.delete(requestId);
137
+ permissionToolUseIDs.delete(requestId);
138
+ console.log(`[perm] resolved: ${requestId} \u2192 ${result.behavior}`);
121
139
  origResolve(result);
122
140
  });
123
141
  });
@@ -145,21 +163,22 @@ var createChat = async ({
145
163
  model,
146
164
  permission,
147
165
  repo,
148
- prompt
166
+ prompt,
167
+ tempId
149
168
  }) => {
150
169
  const repoPath = repo ? await setupRepo(repo) : void 0;
151
- const tempId = `temp_${Date.now()}`;
170
+ const id = tempId || `temp_${Date.now()}`;
152
171
  const mode = permission || "acceptEdits";
153
172
  const session = await withCwd(
154
173
  repoPath,
155
174
  () => unstable_v2_createSession({
156
175
  model: model || "claude-opus-4-6",
157
176
  permissionMode: mode,
158
- canUseTool: makeCanUseTool(tempId, mode)
177
+ canUseTool: makeCanUseTool(id, mode)
159
178
  })
160
179
  );
161
- if (repoPath) sessionCwds.set(tempId, repoPath);
162
- return { session, tempId, repoPath };
180
+ if (repoPath) sessionCwds.set(id, repoPath);
181
+ return { session, tempId: id, repoPath };
163
182
  };
164
183
  var sendMessage = async ({
165
184
  prompt,
@@ -173,31 +192,26 @@ var getSession = async ({
173
192
  permission,
174
193
  cwd
175
194
  }) => {
176
- let session = sessions.get(sessionId);
177
- const currentConfig = sessionConfig.get(sessionId);
178
- const requestedModel = model || "claude-opus-4-6";
179
- const requestedPermission = permission || "acceptEdits";
180
- const configChanged = session && currentConfig && (requestedModel !== currentConfig.model || requestedPermission !== currentConfig.permission);
181
- if (session && configChanged) {
182
- session.close();
195
+ const existing = sessions.get(sessionId);
196
+ if (existing) {
197
+ existing.close();
183
198
  sessions.delete(sessionId);
184
199
  sessionConfig.delete(sessionId);
185
- session = void 0;
186
- }
187
- if (!session) {
188
- const sessionCwd = cwd || sessionCwds.get(sessionId) || await getCwdFromJSONL(sessionId);
189
- session = await withCwd(
190
- sessionCwd,
191
- () => unstable_v2_resumeSession(sessionId, {
192
- model: requestedModel,
193
- permissionMode: requestedPermission,
194
- canUseTool: makeCanUseTool(sessionId, requestedPermission)
195
- })
196
- );
197
- sessions.set(sessionId, session);
198
- sessionConfig.set(sessionId, { model: requestedModel, permission: requestedPermission });
199
- if (sessionCwd) sessionCwds.set(sessionId, sessionCwd);
200
200
  }
201
+ const requestedModel = model || "claude-opus-4-6";
202
+ const requestedPermission = permission || "acceptEdits";
203
+ const sessionCwd = cwd || sessionCwds.get(sessionId) || await getCwdFromJSONL(sessionId);
204
+ const session = await withCwd(
205
+ sessionCwd,
206
+ () => unstable_v2_resumeSession(sessionId, {
207
+ model: requestedModel,
208
+ permissionMode: requestedPermission,
209
+ canUseTool: makeCanUseTool(sessionId, requestedPermission)
210
+ })
211
+ );
212
+ sessions.set(sessionId, session);
213
+ sessionConfig.set(sessionId, { model: requestedModel, permission: requestedPermission });
214
+ if (sessionCwd) sessionCwds.set(sessionId, sessionCwd);
201
215
  return session;
202
216
  };
203
217
 
@@ -365,13 +379,14 @@ chats.post("/new", async (c) => {
365
379
  const { prompt, model, permission, repo } = await c.req.json();
366
380
  if (!prompt) return c.json({ error: "Prompt is required" }, 400);
367
381
  try {
368
- const { session, tempId, repoPath } = await createChat({ model, permission, repo, prompt });
369
- await sendMessage({ prompt, session });
370
- return streamSSE(c, async (stream) => {
371
- let finalSessionId = "";
372
- const sendPerm = async (req) => {
382
+ const tempId = `temp_${Date.now()}`;
383
+ let finalSessionId = "";
384
+ const pendingPermQueue = [];
385
+ let sseStream = null;
386
+ const permCallback = async (req) => {
387
+ if (sseStream) {
373
388
  try {
374
- await stream.writeSSE({
389
+ await sseStream.writeSSE({
375
390
  data: JSON.stringify({
376
391
  type: "permission_request",
377
392
  ...req,
@@ -381,14 +396,31 @@ chats.post("/new", async (c) => {
381
396
  } catch (e) {
382
397
  console.error("Failed to send permission SSE:", e);
383
398
  }
384
- };
385
- setPermissionCallback(tempId, sendPerm);
399
+ } else {
400
+ pendingPermQueue.push(req);
401
+ }
402
+ };
403
+ setPermissionCallback(tempId, permCallback);
404
+ const { session, repoPath } = await createChat({ model, permission, repo, prompt, tempId });
405
+ await sendMessage({ prompt, session });
406
+ return streamSSE(c, async (stream) => {
407
+ sseStream = stream;
408
+ for (const req of pendingPermQueue) {
409
+ try {
410
+ await stream.writeSSE({
411
+ data: JSON.stringify({ type: "permission_request", ...req, sessionId: finalSessionId || "pending" })
412
+ });
413
+ } catch (e) {
414
+ console.error(`[perm] flush failed: ${req.requestId}`, e);
415
+ }
416
+ }
417
+ pendingPermQueue.length = 0;
386
418
  try {
387
419
  for await (const msg of session.stream()) {
388
420
  if (msg.session_id && !finalSessionId) {
389
421
  finalSessionId = msg.session_id;
390
422
  activeSessions.add(finalSessionId);
391
- setPermissionCallback(finalSessionId, sendPerm);
423
+ setPermissionCallback(finalSessionId, permCallback);
392
424
  }
393
425
  const text = getAssistantText(msg);
394
426
  if (text)
@@ -433,26 +465,50 @@ chats.post("/:id/message", async (c) => {
433
465
  if (!prompt || !sessionId || /[\/\\]/.test(sessionId))
434
466
  return c.json({ error: "Invalid request" }, 400);
435
467
  try {
468
+ const pendingPermQueue = [];
469
+ let sseStream = null;
470
+ setPermissionCallback(sessionId, async (req) => {
471
+ if (sseStream) {
472
+ try {
473
+ await sseStream.writeSSE({
474
+ data: JSON.stringify({ type: "permission_request", ...req, sessionId })
475
+ });
476
+ } catch (e) {
477
+ console.error(`[perm] SSE write failed: ${req.requestId}`, e);
478
+ }
479
+ } else {
480
+ pendingPermQueue.push(req);
481
+ }
482
+ });
436
483
  const session = await getSession({ sessionId, model, permission });
437
484
  if (!session) return c.json({ error: "Session is required" }, 400);
438
485
  await sendMessage({ prompt, session });
439
486
  return streamSSE(c, async (stream) => {
440
487
  activeSessions.add(sessionId);
441
- setPermissionCallback(sessionId, async (req) => {
488
+ sseStream = stream;
489
+ for (const req of pendingPermQueue) {
442
490
  try {
443
491
  await stream.writeSSE({
444
- data: JSON.stringify({
445
- type: "permission_request",
446
- ...req,
447
- sessionId
448
- })
492
+ data: JSON.stringify({ type: "permission_request", ...req, sessionId })
449
493
  });
450
494
  } catch (e) {
451
- console.error("Failed to send permission SSE:", e);
495
+ console.error(`[perm] flush failed: ${req.requestId}`, e);
452
496
  }
453
- });
497
+ }
498
+ pendingPermQueue.length = 0;
454
499
  try {
455
500
  for await (const msg of session.stream()) {
501
+ const blocks = msg.message?.content;
502
+ if (Array.isArray(blocks)) {
503
+ for (const b of blocks) {
504
+ if (b.type === "tool_use") {
505
+ console.log(`[stream] tool_use: ${b.name}`);
506
+ } else if (b.type === "tool_result" && b.is_error) {
507
+ const resultText = Array.isArray(b.content) ? b.content.filter((r) => r.type === "text").map((r) => r.text).join("").slice(0, 1e3) : String(b.content ?? "").slice(0, 1e3);
508
+ console.log(`[stream] tool_result ERROR: ${resultText.slice(0, 500)}`);
509
+ }
510
+ }
511
+ }
456
512
  const text = getAssistantText(msg);
457
513
  if (text)
458
514
  await stream.writeSSE({
@@ -470,6 +526,7 @@ chats.post("/:id/message", async (c) => {
470
526
  generateChatName(prompt, sessionId).catch(() => {
471
527
  });
472
528
  } finally {
529
+ sseStream = null;
473
530
  setPermissionCallback(sessionId, null);
474
531
  activeSessions.delete(sessionId);
475
532
  }
@@ -484,13 +541,13 @@ chats.post("/:id/permission", async (c) => {
484
541
  if (!requestId || !behavior)
485
542
  return c.json({ error: "requestId and behavior are required" }, 400);
486
543
  const resolve = pendingPermissions.get(requestId);
487
- if (!resolve)
544
+ if (!resolve) {
545
+ console.log(`[perm] no pending request: ${requestId}`);
488
546
  return c.json({ error: "No pending permission request found" }, 404);
547
+ }
489
548
  if (behavior === "allow") {
490
- resolve({
491
- behavior: "allow",
492
- ...updatedPermissions ? { updatedPermissions } : {}
493
- });
549
+ const originalInput = permissionInputs.get(requestId) || {};
550
+ resolve({ behavior: "allow", updatedInput: originalInput, ...updatedPermissions ? { updatedPermissions } : {} });
494
551
  } else {
495
552
  resolve({ behavior: "deny", message: "User denied permission" });
496
553
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "anywhere-ai",
3
- "version": "0.0.26",
3
+ "version": "0.0.27",
4
4
  "type": "module",
5
5
  "description": "Code on any repo from your phone",
6
6
  "bin": {