anywhere-ai 0.0.26 → 0.0.28

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.28"}`);
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.28"} \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,48 @@ 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_result" && b.is_error) {
505
+ 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);
506
+ console.log(`[stream] tool_result ERROR: ${resultText.slice(0, 500)}`);
507
+ }
508
+ }
509
+ }
456
510
  const text = getAssistantText(msg);
457
511
  if (text)
458
512
  await stream.writeSSE({
@@ -470,6 +524,7 @@ chats.post("/:id/message", async (c) => {
470
524
  generateChatName(prompt, sessionId).catch(() => {
471
525
  });
472
526
  } finally {
527
+ sseStream = null;
473
528
  setPermissionCallback(sessionId, null);
474
529
  activeSessions.delete(sessionId);
475
530
  }
@@ -484,13 +539,13 @@ chats.post("/:id/permission", async (c) => {
484
539
  if (!requestId || !behavior)
485
540
  return c.json({ error: "requestId and behavior are required" }, 400);
486
541
  const resolve = pendingPermissions.get(requestId);
487
- if (!resolve)
542
+ if (!resolve) {
543
+ console.log(`[perm] no pending request: ${requestId}`);
488
544
  return c.json({ error: "No pending permission request found" }, 404);
545
+ }
489
546
  if (behavior === "allow") {
490
- resolve({
491
- behavior: "allow",
492
- ...updatedPermissions ? { updatedPermissions } : {}
493
- });
547
+ const originalInput = permissionInputs.get(requestId) || {};
548
+ resolve({ behavior: "allow", updatedInput: originalInput, ...updatedPermissions ? { updatedPermissions } : {} });
494
549
  } else {
495
550
  resolve({ behavior: "deny", message: "User denied permission" });
496
551
  }
@@ -842,19 +897,15 @@ gh.get("/repos", async (c) => {
842
897
  gh.get("/pr", async (c) => {
843
898
  try {
844
899
  const cwd = c.req.query("cwd");
845
- console.log("[gh/pr GET] cwd:", cwd);
846
900
  const root = await getRepoRoot(cwd);
847
- console.log("[gh/pr GET] resolved root:", root);
848
901
  if (!root) return c.json({ exists: false });
849
902
  const { stdout } = await execAsync2(
850
903
  "gh pr view --json number,title,url",
851
904
  { cwd: root }
852
905
  );
853
906
  const pr = JSON.parse(stdout);
854
- console.log("[gh/pr GET] found PR:", pr);
855
907
  return c.json({ exists: true, pr });
856
- } catch (err) {
857
- console.log("[gh/pr GET] no PR found:", err?.stderr?.trim() || err?.message);
908
+ } catch {
858
909
  return c.json({ exists: false });
859
910
  }
860
911
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "anywhere-ai",
3
- "version": "0.0.26",
3
+ "version": "0.0.28",
4
4
  "type": "module",
5
5
  "description": "Code on any repo from your phone",
6
6
  "bin": {