@hua-labs/tap 0.4.2 → 0.5.1

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.
@@ -1,8 +1,8 @@
1
1
  // src/bridges/codex-app-server-bridge.ts
2
- import { pathToFileURL as pathToFileURL3 } from "url";
3
- import { resolve as resolve6 } from "path";
2
+ import { pathToFileURL as pathToFileURL2 } from "url";
3
+ import { resolve as resolve5 } from "path";
4
4
 
5
- // ../../scripts/bridge/bridge-types.ts
5
+ // scripts/bridge/bridge-types.ts
6
6
  var DEFAULT_AGENT = String.fromCharCode(50728);
7
7
  var DEFAULT_APP_SERVER_URL = "ws://127.0.0.1:4501";
8
8
  var AUTH_SUBPROTOCOL_PREFIX = "tap-auth-";
@@ -30,11 +30,11 @@ var COMMS_HEARTBEAT_LOCK_TIMEOUT_MS = 2e3;
30
30
  var COMMS_LOCK_STALE_AGE_MS = 1e4;
31
31
  var STALE_TURN_MS = 5 * 60 * 1e3;
32
32
 
33
- // ../../scripts/bridge/bridge-routing.ts
33
+ // scripts/bridge/bridge-routing.ts
34
34
  import { existsSync, readFileSync, writeFileSync } from "fs";
35
35
  import { join, resolve } from "path";
36
36
 
37
- // ../tap-plugin/channels/tap-identity.ts
37
+ // packages/tap-plugin/channels/tap-identity.ts
38
38
  var BROADCAST_RECIPIENTS = /* @__PURE__ */ new Set(["\uC804\uCCB4", "all"]);
39
39
  function trimAddress(value) {
40
40
  return value?.trim() ?? "";
@@ -71,7 +71,7 @@ function isOwnMessageAddress(sender, agentId, agentName) {
71
71
  return sameRoutingAddress(normalizedSender, agentId) || normalizedSender === trimAddress(agentName);
72
72
  }
73
73
 
74
- // ../../scripts/bridge/bridge-routing.ts
74
+ // scripts/bridge/bridge-routing.ts
75
75
  function canonicalize(id) {
76
76
  return canonicalizeAgentId(id);
77
77
  }
@@ -85,9 +85,12 @@ function threadCwdMatches(expectedCwd, actualCwd) {
85
85
  return normalizeThreadCwd(expectedCwd) === normalizeThreadCwd(actualCwd);
86
86
  }
87
87
  function chooseLoadedThreadForCwd(cwd, threads) {
88
- const matching = threads.filter(
89
- (thread) => threadCwdMatches(cwd, thread.cwd)
90
- );
88
+ const matching = threads.filter((thread) => {
89
+ if (!threadCwdMatches(cwd, thread.cwd)) {
90
+ return false;
91
+ }
92
+ return thread.statusType !== "notLoaded";
93
+ });
91
94
  if (matching.length === 0) {
92
95
  return null;
93
96
  }
@@ -237,7 +240,7 @@ function getInboxRouteFromFilename(fileName) {
237
240
  };
238
241
  }
239
242
 
240
- // ../../scripts/bridge/bridge-config.ts
243
+ // scripts/bridge/bridge-config.ts
241
244
  import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync3 } from "fs";
242
245
  import { isAbsolute as isAbsolute2, join as join3, resolve as resolve3 } from "path";
243
246
 
@@ -258,7 +261,7 @@ function normalizeTapPath(input) {
258
261
  return trimmed;
259
262
  }
260
263
 
261
- // ../../scripts/bridge/bridge-config.ts
264
+ // scripts/bridge/bridge-config.ts
262
265
  function ensureDir(target) {
263
266
  if (!existsSync3(target)) {
264
267
  mkdirSync(target, { recursive: true });
@@ -562,12 +565,12 @@ function buildOptions(argv) {
562
565
  };
563
566
  }
564
567
 
565
- // ../../scripts/bridge/bridge-candidates.ts
568
+ // scripts/bridge/bridge-candidates.ts
566
569
  import { createHash } from "crypto";
567
570
  import { existsSync as existsSync4, readFileSync as readFileSync4, readdirSync, statSync } from "fs";
568
571
  import { join as join4 } from "path";
569
572
 
570
- // ../../scripts/bridge/bridge-logging.ts
573
+ // scripts/bridge/bridge-logging.ts
571
574
  var LOG_LEVEL_PRIORITY = {
572
575
  debug: 10,
573
576
  info: 20,
@@ -639,7 +642,7 @@ function createBridgeLogger(scope) {
639
642
  };
640
643
  }
641
644
 
642
- // ../../scripts/bridge/bridge-candidates.ts
645
+ // scripts/bridge/bridge-candidates.ts
643
646
  var routingLogger = createBridgeLogger("routing");
644
647
  function buildMarkerId(filePath, mtimeMs) {
645
648
  return createHash("sha1").update(`${filePath}|${mtimeMs}`).digest("hex");
@@ -748,7 +751,7 @@ function getPendingCandidates(options, cutoff) {
748
751
  return { heartbeats, candidates };
749
752
  }
750
753
 
751
- // ../../scripts/bridge/bridge-format.ts
754
+ // scripts/bridge/bridge-format.ts
752
755
  import { writeFileSync as writeFileSync3 } from "fs";
753
756
  import { join as join5 } from "path";
754
757
  function buildUserInput(candidate, agentName, heartbeats) {
@@ -817,7 +820,7 @@ function writeLastDispatch(stateDir, candidate, dispatchMode, threadId, turnId)
817
820
  );
818
821
  }
819
822
 
820
- // ../../scripts/bridge/bridge-dispatch.ts
823
+ // scripts/bridge/bridge-dispatch.ts
821
824
  import {
822
825
  existsSync as existsSync5,
823
826
  readFileSync as readFileSync5,
@@ -916,13 +919,18 @@ function updateCommsHeartbeat(options, status) {
916
919
  }
917
920
  const key = options.agentId;
918
921
  const existing = store[key];
922
+ const now = (/* @__PURE__ */ new Date()).toISOString();
919
923
  store[key] = {
920
924
  id: options.agentId,
921
925
  agent: options.agentName,
922
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
923
- lastActivity: (/* @__PURE__ */ new Date()).toISOString(),
924
- joinedAt: existing?.joinedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
925
- status
926
+ timestamp: now,
927
+ lastActivity: now,
928
+ joinedAt: existing?.joinedAt ?? now,
929
+ status,
930
+ source: "bridge-dispatch",
931
+ instanceId: options.agentId,
932
+ bridgePid: process.pid,
933
+ connectHash: `instance:${options.agentId}`
926
934
  };
927
935
  const tmpPath = heartbeatsPath + ".tmp." + process.pid;
928
936
  writeFileSync4(tmpPath, JSON.stringify(store, null, 2), "utf-8");
@@ -933,7 +941,54 @@ function updateCommsHeartbeat(options, status) {
933
941
  }
934
942
  }
935
943
  var heartbeatCount = 0;
944
+ function readPreviousHeartbeat(stateDir) {
945
+ const heartbeatPath = join6(stateDir, "heartbeat.json");
946
+ if (!existsSync5(heartbeatPath)) {
947
+ return null;
948
+ }
949
+ try {
950
+ return JSON.parse(
951
+ readFileSync5(heartbeatPath, "utf8")
952
+ );
953
+ } catch {
954
+ return null;
955
+ }
956
+ }
957
+ function readLastDispatchAt(stateDir) {
958
+ const dispatchPath = join6(stateDir, "last-dispatch.json");
959
+ if (!existsSync5(dispatchPath)) {
960
+ return null;
961
+ }
962
+ try {
963
+ const parsed = JSON.parse(
964
+ readFileSync5(dispatchPath, "utf8")
965
+ );
966
+ return typeof parsed.dispatchedAt === "string" ? parsed.dispatchedAt : null;
967
+ } catch {
968
+ return null;
969
+ }
970
+ }
971
+ function isWaitingApprovalStatus(status) {
972
+ if (!status) return false;
973
+ return /approval|input-required|confirm|consent/i.test(status);
974
+ }
975
+ function resolveTurnState(client) {
976
+ if (!client) return null;
977
+ if (client.activeTurnId) return "active";
978
+ if (client.connected === false) return "disconnected";
979
+ if (isWaitingApprovalStatus(client.lastTurnStatus)) {
980
+ return "waiting-approval";
981
+ }
982
+ if (client.connected) return "idle";
983
+ return null;
984
+ }
936
985
  function writeHeartbeat(options, client, health) {
986
+ const nowIso = (/* @__PURE__ */ new Date()).toISOString();
987
+ const previousHeartbeat = readPreviousHeartbeat(options.stateDir);
988
+ const lastDispatchAt = readLastDispatchAt(options.stateDir);
989
+ const turnState = resolveTurnState(client);
990
+ const lastTurnAt = previousHeartbeat?.activeTurnId && !client?.activeTurnId ? nowIso : previousHeartbeat?.lastTurnAt ?? null;
991
+ const idleSince = turnState === "idle" || turnState === "waiting-approval" ? previousHeartbeat?.turnState === turnState && previousHeartbeat.idleSince ? previousHeartbeat.idleSince : lastTurnAt ?? lastDispatchAt ?? nowIso : null;
937
992
  if (client?.threadId) {
938
993
  const savedThread = readThreadState(options.stateDir);
939
994
  persistThreadState(
@@ -947,7 +1002,7 @@ function writeHeartbeat(options, client, health) {
947
1002
  const payload = {
948
1003
  pid: process.pid,
949
1004
  agent: options.agentName,
950
- updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
1005
+ updatedAt: nowIso,
951
1006
  pollSeconds: options.pollSeconds,
952
1007
  appServerUrl: options.appServerUrl,
953
1008
  authenticated: Boolean(options.gatewayToken),
@@ -958,6 +1013,10 @@ function writeHeartbeat(options, client, health) {
958
1013
  activeTurnId: client?.activeTurnId ?? null,
959
1014
  turnStartedAt: client?.turnStartedAt ?? null,
960
1015
  lastTurnStatus: client?.lastTurnStatus ?? null,
1016
+ lastTurnAt,
1017
+ lastDispatchAt,
1018
+ idleSince,
1019
+ turnState: turnState ?? void 0,
961
1020
  lastNotificationMethod: client?.lastNotificationMethod ?? null,
962
1021
  lastNotificationAt: client?.lastNotificationAt ?? null,
963
1022
  lastError: sanitizeErrorForPersistence(client?.lastError ?? null),
@@ -981,7 +1040,7 @@ function writeHeartbeat(options, client, health) {
981
1040
  consecutiveFailureCount: payload.consecutiveFailureCount
982
1041
  });
983
1042
  }
984
- const status = client?.connected ? "active" : "idle";
1043
+ const status = turnState === "active" ? "active" : "idle";
985
1044
  updateCommsHeartbeat(options, status);
986
1045
  }
987
1046
  async function dispatchCandidate(client, options, candidate, heartbeats) {
@@ -1181,7 +1240,7 @@ async function maybeBootstrapHeadlessTurn(options, cutoff, client) {
1181
1240
  }
1182
1241
  }
1183
1242
 
1184
- // ../../scripts/bridge/bridge-ws-client.ts
1243
+ // scripts/bridge/bridge-ws-client.ts
1185
1244
  async function readSocketData(data) {
1186
1245
  if (typeof data === "string") {
1187
1246
  return data;
@@ -1729,7 +1788,7 @@ var AppServerClient = class {
1729
1788
  }
1730
1789
  };
1731
1790
 
1732
- // ../../scripts/bridge/bridge-main.ts
1791
+ // scripts/bridge/bridge-main.ts
1733
1792
  import { existsSync as existsSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
1734
1793
  import { isAbsolute as isAbsolute3, join as join7, resolve as resolve4 } from "path";
1735
1794
  import { pathToFileURL } from "url";
@@ -1948,29 +2007,13 @@ function isDirectExecution() {
1948
2007
  return import.meta.url === pathToFileURL(resolve4(entry)).href;
1949
2008
  }
1950
2009
 
1951
- // ../../scripts/codex-app-server-bridge.ts
1952
- import { resolve as resolve5 } from "path";
1953
- import { pathToFileURL as pathToFileURL2 } from "url";
2010
+ // src/bridges/codex-app-server-bridge.ts
1954
2011
  function isDirectExecution2() {
1955
2012
  const entry = process.argv[1];
1956
2013
  if (!entry) return false;
1957
2014
  return import.meta.url === pathToFileURL2(resolve5(entry)).href;
1958
2015
  }
1959
2016
  if (isDirectExecution2()) {
1960
- main().catch((error) => {
1961
- const raw = error instanceof Error ? error.stack ?? error.message : String(error);
1962
- console.error(sanitizeErrorForPersistence(raw));
1963
- process.exitCode = 1;
1964
- });
1965
- }
1966
-
1967
- // src/bridges/codex-app-server-bridge.ts
1968
- function isDirectExecution3() {
1969
- const entry = process.argv[1];
1970
- if (!entry) return false;
1971
- return import.meta.url === pathToFileURL3(resolve6(entry)).href;
1972
- }
1973
- if (isDirectExecution3()) {
1974
2017
  main().catch((error) => {
1975
2018
  console.error(
1976
2019
  error instanceof Error ? error.stack ?? error.message : String(error)