@standardagents/builder 0.40.2 → 0.40.4

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/runtime.d.ts CHANGED
@@ -1230,6 +1230,7 @@ declare class DurableThread<Env extends ThreadEnv = ThreadEnv> extends DurableOb
1230
1230
  */
1231
1231
  deliverClientToolResult(toolCallId: string, ok: boolean, result?: string, error?: string, deliveredByClientId?: string, deliveredGeneration?: number, deliveryToken?: string, internalAuthorization?: typeof INTERNAL_TOOL_DELIVERY): Promise<{
1232
1232
  side: "a" | "b";
1233
+ resume: boolean;
1233
1234
  } | null>;
1234
1235
  /** Default time to wait for the client to accept a stream open. */
1235
1236
  private static readonly CLIENT_STREAM_OPEN_TIMEOUT_MS;
@@ -2787,8 +2788,10 @@ declare class DurableAgentBuilder<Env extends AgentBuilderEnv = AgentBuilderEnv>
2787
2788
  /** Deny a pending device login without creating a user or API key. */
2788
2789
  denyDeviceLogin(codeHash: string): Promise<boolean>;
2789
2790
  /** Approve a pending device login: mint a personal API key for the user and
2790
- * park its plaintext for the single claim. Returns false when the code is
2791
- * unknown, expired, or not pending. */
2791
+ * park its plaintext for the single claim. A retry by the same user remains
2792
+ * successful after the row is approved or claimed, without minting another
2793
+ * key. Returns false for unknown, expired, denied, or differently-owned
2794
+ * logins. */
2792
2795
  approveDeviceLogin(params: {
2793
2796
  code_hash: string;
2794
2797
  user_id: string;
package/dist/runtime.js CHANGED
@@ -21902,17 +21902,23 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
21902
21902
  return cursor.rowsWritten > 0;
21903
21903
  }
21904
21904
  /** Approve a pending device login: mint a personal API key for the user and
21905
- * park its plaintext for the single claim. Returns false when the code is
21906
- * unknown, expired, or not pending. */
21905
+ * park its plaintext for the single claim. A retry by the same user remains
21906
+ * successful after the row is approved or claimed, without minting another
21907
+ * key. Returns false for unknown, expired, denied, or differently-owned
21908
+ * logins. */
21907
21909
  async approveDeviceLogin(params) {
21908
21910
  await this.ensureMigrated();
21909
21911
  await this.sweepDeviceLogins();
21910
21912
  const cursor = await this.ctx.storage.sql.exec(
21911
- `SELECT status FROM device_logins WHERE code_hash = ?`,
21913
+ `SELECT status, user_id FROM device_logins WHERE code_hash = ?`,
21912
21914
  params.code_hash
21913
21915
  );
21914
21916
  const row = cursor.toArray()[0];
21915
- if (!row || row.status !== "pending") return false;
21917
+ if (!row) return false;
21918
+ if (row.status === "approved" || row.status === "claimed") {
21919
+ return row.user_id === params.user_id;
21920
+ }
21921
+ if (row.status !== "pending") return false;
21916
21922
  await this.createApiKey({
21917
21923
  name: params.key_name,
21918
21924
  key_hash: params.token_hash,
@@ -26071,7 +26077,7 @@ ${resultContent}${attachmentSummary}`;
26071
26077
  void 0,
26072
26078
  INTERNAL_TOOL_DELIVERY
26073
26079
  );
26074
- if (delivered) sides.add(delivered.side);
26080
+ if (delivered?.resume) sides.add(delivered.side);
26075
26081
  }
26076
26082
  if (threadId) {
26077
26083
  for (const side of sides) {
@@ -26373,14 +26379,17 @@ ${resultContent}${attachmentSummary}`;
26373
26379
  deliveredResultWaiters = /* @__PURE__ */ new Map();
26374
26380
  notifyDeliveredResultWaiters(toolCallId) {
26375
26381
  const waiters = this.deliveredResultWaiters?.get(toolCallId);
26376
- if (!waiters) return;
26382
+ if (!waiters || waiters.size === 0) return false;
26377
26383
  this.deliveredResultWaiters.delete(toolCallId);
26384
+ let notified = false;
26378
26385
  for (const wake of waiters) {
26379
26386
  try {
26380
26387
  wake();
26388
+ notified = true;
26381
26389
  } catch {
26382
26390
  }
26383
26391
  }
26392
+ return notified;
26384
26393
  }
26385
26394
  /**
26386
26395
  * Await a parked forwarded call's delivered result: one immediate read,
@@ -26525,37 +26534,46 @@ ${resultContent}${attachmentSummary}`;
26525
26534
  const side = pending.side === "b" ? "b" : "a";
26526
26535
  const content = ok ? result ?? "" : `Error: ${error ?? "tool failed"}`;
26527
26536
  const toolStatus = ok ? "success" : "error";
26528
- await this.ctx.storage.sql.exec(
26529
- `UPDATE messages SET content = ?, tool_status = ? WHERE tool_call_id = ? AND role = 'tool'`,
26530
- content,
26531
- toolStatus,
26532
- toolCallId
26533
- );
26534
- const updated = await this.ctx.storage.sql.exec(
26535
- `SELECT COUNT(*) AS total FROM messages WHERE tool_call_id = ? AND role = 'tool'`,
26536
- toolCallId
26537
- );
26538
- if (Number(updated.toArray()[0]?.total ?? 0) === 0) {
26539
- const toolRow = await this.ctx.storage.sql.exec(
26540
- `SELECT tool FROM pending_client_calls WHERE tool_call_id = ? LIMIT 1`,
26537
+ const resultMessageId = crypto.randomUUID();
26538
+ const resultCreatedAt = Date.now() * TIMESTAMP_MULTIPLIER;
26539
+ const accepted = this.ctx.storage.transactionSync(() => {
26540
+ const claimed = this.ctx.storage.sql.exec(
26541
+ `UPDATE pending_client_calls
26542
+ SET status = 'resolved'
26543
+ WHERE tool_call_id = ? AND status = 'pending'`,
26541
26544
  toolCallId
26542
26545
  );
26543
- await this.ctx.storage.sql.exec(
26544
- `INSERT INTO messages (id, role, content, name, tool_call_id, tool_status, created_at, status, prompt)
26545
- VALUES (?, 'tool', ?, ?, ?, ?, ?, 'completed', NULL)`,
26546
- crypto.randomUUID(),
26546
+ if (claimed.rowsWritten !== 1) return false;
26547
+ this.ctx.storage.sql.exec(
26548
+ `UPDATE messages SET content = ?, tool_status = ? WHERE tool_call_id = ? AND role = 'tool'`,
26547
26549
  content,
26548
- toolRow.toArray()[0]?.tool ?? null,
26549
- toolCallId,
26550
26550
  toolStatus,
26551
- Date.now() * TIMESTAMP_MULTIPLIER
26551
+ toolCallId
26552
26552
  );
26553
- }
26554
- await this.ctx.storage.sql.exec(
26555
- `UPDATE pending_client_calls SET status = 'resolved' WHERE tool_call_id = ?`,
26556
- toolCallId
26557
- );
26558
- this.notifyDeliveredResultWaiters(toolCallId);
26553
+ const updated = this.ctx.storage.sql.exec(
26554
+ `SELECT COUNT(*) AS total FROM messages WHERE tool_call_id = ? AND role = 'tool'`,
26555
+ toolCallId
26556
+ );
26557
+ if (Number(updated.toArray()[0]?.total ?? 0) === 0) {
26558
+ const toolRow = this.ctx.storage.sql.exec(
26559
+ `SELECT tool FROM pending_client_calls WHERE tool_call_id = ? LIMIT 1`,
26560
+ toolCallId
26561
+ );
26562
+ this.ctx.storage.sql.exec(
26563
+ `INSERT INTO messages (id, role, content, name, tool_call_id, tool_status, created_at, status, prompt)
26564
+ VALUES (?, 'tool', ?, ?, ?, ?, ?, 'completed', NULL)`,
26565
+ resultMessageId,
26566
+ content,
26567
+ toolRow.toArray()[0]?.tool ?? null,
26568
+ toolCallId,
26569
+ toolStatus,
26570
+ resultCreatedAt
26571
+ );
26572
+ }
26573
+ return true;
26574
+ });
26575
+ if (!accepted) return null;
26576
+ const resumedInline = this.notifyDeliveredResultWaiters(toolCallId);
26559
26577
  try {
26560
26578
  const msgCursor = await this.ctx.storage.sql.exec(
26561
26579
  `SELECT id, role, content, name, tool_call_id, tool_status, created_at, depth
@@ -26566,7 +26584,7 @@ ${resultContent}${attachmentSummary}`;
26566
26584
  if (msg) this.broadcastMessage(msg);
26567
26585
  } catch {
26568
26586
  }
26569
- return { side };
26587
+ return { side, resume: !resumedInline };
26570
26588
  }
26571
26589
  // ─── Runtime byte streams ──────────────────────────────────────────────────
26572
26590
  // The existing bridge frames carry opaque capability metadata and bytes