fix: goals route now finds devices by persistent DB ID, not connection UUID
This commit is contained in:
@@ -25,7 +25,9 @@ goals.post("/", async (c) => {
|
|||||||
return c.json({ error: "deviceId and goal are required" }, 400);
|
return c.json({ error: "deviceId and goal are required" }, 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
const device = sessions.getDevice(body.deviceId);
|
// Look up by connection ID first, then by persistent DB ID
|
||||||
|
const device = sessions.getDevice(body.deviceId)
|
||||||
|
?? sessions.getDeviceByPersistentId(body.deviceId);
|
||||||
if (!device) {
|
if (!device) {
|
||||||
return c.json({ error: "device not connected" }, 404);
|
return c.json({ error: "device not connected" }, 404);
|
||||||
}
|
}
|
||||||
@@ -35,8 +37,9 @@ goals.post("/", async (c) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prevent multiple agent loops on the same device
|
// Prevent multiple agent loops on the same device
|
||||||
if (activeSessions.has(body.deviceId)) {
|
const trackingKey = device.persistentDeviceId ?? device.deviceId;
|
||||||
const existing = activeSessions.get(body.deviceId)!;
|
if (activeSessions.has(trackingKey)) {
|
||||||
|
const existing = activeSessions.get(trackingKey)!;
|
||||||
return c.json(
|
return c.json(
|
||||||
{ error: "agent already running on this device", sessionId: existing.sessionId, goal: existing.goal },
|
{ error: "agent already running on this device", sessionId: existing.sessionId, goal: existing.goal },
|
||||||
409
|
409
|
||||||
@@ -55,7 +58,8 @@ goals.post("/", async (c) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const options: AgentLoopOptions = {
|
const options: AgentLoopOptions = {
|
||||||
deviceId: body.deviceId,
|
deviceId: device.deviceId,
|
||||||
|
persistentDeviceId: device.persistentDeviceId,
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
goal: body.goal,
|
goal: body.goal,
|
||||||
llmConfig,
|
llmConfig,
|
||||||
@@ -67,20 +71,19 @@ goals.post("/", async (c) => {
|
|||||||
const loopPromise = runAgentLoop(options);
|
const loopPromise = runAgentLoop(options);
|
||||||
|
|
||||||
// Track as active until it completes
|
// Track as active until it completes
|
||||||
const trackingId = body.deviceId;
|
|
||||||
const sessionPlaceholder = { sessionId: "pending", goal: body.goal };
|
const sessionPlaceholder = { sessionId: "pending", goal: body.goal };
|
||||||
activeSessions.set(trackingId, sessionPlaceholder);
|
activeSessions.set(trackingKey, sessionPlaceholder);
|
||||||
|
|
||||||
loopPromise
|
loopPromise
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
activeSessions.delete(trackingId);
|
activeSessions.delete(trackingKey);
|
||||||
console.log(
|
console.log(
|
||||||
`[Agent] Completed on ${body.deviceId}: ${result.success ? "success" : "incomplete"} in ${result.stepsUsed} steps (session ${result.sessionId})`
|
`[Agent] Completed on ${device.deviceId}: ${result.success ? "success" : "incomplete"} in ${result.stepsUsed} steps (session ${result.sessionId})`
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
activeSessions.delete(trackingId);
|
activeSessions.delete(trackingKey);
|
||||||
console.error(`[Agent] Error on ${body.deviceId}: ${err}`);
|
console.error(`[Agent] Error on ${device.deviceId}: ${err}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
// We need the sessionId from the loop, but it's created inside runAgentLoop.
|
// We need the sessionId from the loop, but it's created inside runAgentLoop.
|
||||||
|
|||||||
@@ -122,6 +122,7 @@ export async function handleDeviceMessage(
|
|||||||
// Register device in session manager
|
// Register device in session manager
|
||||||
sessions.addDevice({
|
sessions.addDevice({
|
||||||
deviceId,
|
deviceId,
|
||||||
|
persistentDeviceId,
|
||||||
userId,
|
userId,
|
||||||
ws,
|
ws,
|
||||||
deviceInfo: msg.deviceInfo,
|
deviceInfo: msg.deviceInfo,
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ export interface WebSocketData {
|
|||||||
/** A connected Android device */
|
/** A connected Android device */
|
||||||
export interface ConnectedDevice {
|
export interface ConnectedDevice {
|
||||||
deviceId: string;
|
deviceId: string;
|
||||||
|
persistentDeviceId?: string;
|
||||||
userId: string;
|
userId: string;
|
||||||
ws: ServerWebSocket<WebSocketData>;
|
ws: ServerWebSocket<WebSocketData>;
|
||||||
deviceInfo?: DeviceInfo;
|
deviceInfo?: DeviceInfo;
|
||||||
@@ -56,6 +57,16 @@ class SessionManager {
|
|||||||
return this.devices.get(deviceId);
|
return this.devices.get(deviceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Look up a device by its persistent DB ID (survives reconnects) */
|
||||||
|
getDeviceByPersistentId(persistentId: string): ConnectedDevice | undefined {
|
||||||
|
for (const device of this.devices.values()) {
|
||||||
|
if (device.persistentDeviceId === persistentId) {
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
getDevicesForUser(userId: string): ConnectedDevice[] {
|
getDevicesForUser(userId: string): ConnectedDevice[] {
|
||||||
const result: ConnectedDevice[] = [];
|
const result: ConnectedDevice[] = [];
|
||||||
for (const device of this.devices.values()) {
|
for (const device of this.devices.values()) {
|
||||||
|
|||||||
@@ -370,7 +370,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Live Steps -->
|
<!-- Live Steps -->
|
||||||
{#if steps.length > 0 || runStatus === 'running'}
|
{#if steps.length > 0 || runStatus !== 'idle'}
|
||||||
<div class="rounded-lg border border-neutral-200">
|
<div class="rounded-lg border border-neutral-200">
|
||||||
<div
|
<div
|
||||||
class="flex items-center justify-between border-b border-neutral-200 px-5 py-3"
|
class="flex items-center justify-between border-b border-neutral-200 px-5 py-3"
|
||||||
|
|||||||
Reference in New Issue
Block a user