@lightupai/polaris 0.0.110 → 0.0.114

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.
@@ -41,7 +41,7 @@ if [ -f "$INSTALLED_PKG" ]; then
41
41
  NOW=$(date +%s 2>/dev/null || echo "0")
42
42
  CACHE_AGE=$(( NOW - CACHE_TS )) 2>/dev/null || CACHE_AGE=9999
43
43
  STALE=false
44
- [ "$CACHE_AGE" -gt 3600 ] 2>/dev/null && STALE=true
44
+ [ "$CACHE_AGE" -gt 300 ] 2>/dev/null && STALE=true
45
45
  else
46
46
  STALE=true
47
47
  fi
@@ -72,11 +72,26 @@ if [ "$CONNECTED" = "true" ]; then
72
72
  if [ -n "$ACCOUNT" ]; then
73
73
  ACCOUNT_TAG="[${ACCOUNT}] "
74
74
  fi
75
- if [ -n "$SLACK" ]; then
76
- echo "polaris: ${ACCOUNT_TAG}${PROJECT}/${SESSION} (${USER}) #${SLACK}${UPDATE_MSG}"
77
- else
78
- echo "polaris: ${ACCOUNT_TAG}${PROJECT}/${SESSION} (${USER})${UPDATE_MSG}"
75
+
76
+ # Liveness indicator: seconds since last delivered event
77
+ RECENCY=""
78
+ LAST_AGE=$(echo "$STATUS" | jq -r '.lastEventAge // ""' 2>/dev/null || echo "")
79
+ if [ -n "$LAST_AGE" ] && [ "$LAST_AGE" != "null" ]; then
80
+ if [ "$LAST_AGE" -lt 60 ] 2>/dev/null; then
81
+ RECENCY=" · just now"
82
+ elif [ "$LAST_AGE" -lt 3600 ] 2>/dev/null; then
83
+ RECENCY=" · $(( LAST_AGE / 60 ))m ago"
84
+ else
85
+ RECENCY=" · $(( LAST_AGE / 3600 ))h ago"
86
+ fi
87
+ fi
88
+
89
+ # Show Slack channel only when it differs from the project name (adds info, avoids duplication)
90
+ SLACK_TAG=""
91
+ if [ -n "$SLACK" ] && [ "$SLACK" != "$PROJECT" ]; then
92
+ SLACK_TAG=" → #${SLACK}"
79
93
  fi
94
+ echo "polaris: ${ACCOUNT_TAG}#${PROJECT}/${SESSION} (${USER})${SLACK_TAG}${RECENCY}${UPDATE_MSG}"
80
95
  else
81
96
  echo "polaris: not connected${UPDATE_MSG}"
82
97
  fi
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightupai/polaris",
3
- "version": "0.0.110",
3
+ "version": "0.0.114",
4
4
  "description": "Record and stream Claude Code sessions to Slack. Like Gong for AI coding — capture every prompt, response, and tool call. Multiplayer collaboration across AI coding agents.",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -707,6 +707,17 @@ export function startDaemon(port = Number(process.env.POLARIS_DAEMON_PORT ?? 432
707
707
  };
708
708
  sessions.set(body.ccSessionId, mapping);
709
709
 
710
+ // Evict stale alias entries that share this (project, session) pair.
711
+ // After a reconnect the old CC session ID still has an entry with the
712
+ // same pair; leaving it allows hook events from the old shell to
713
+ // alias-claim a future pending session or send duplicate events.
714
+ for (const [id, m] of sessions) {
715
+ if (id !== body.ccSessionId && m.project === mapping.project && m.session === mapping.session) {
716
+ sessions.delete(id);
717
+ console.error(`[daemon] Evicted stale alias ${id.slice(0, 8)} for ${mapping.project}/${mapping.session}`);
718
+ }
719
+ }
720
+
710
721
  // Ensure the session exists (create if not, claim driver)
711
722
  // Retry with new name on 409 (collision with generated name)
712
723
  let attempts = 0;
@@ -850,10 +861,16 @@ export function startDaemon(port = Number(process.env.POLARIS_DAEMON_PORT ?? 432
850
861
  // ran polaris_connect. Always returns not_connected; hooks from
851
862
  // unjoined shells are never auto-routed to an existing session.
852
863
  let mapping = sessions.get(ccSessionId);
853
- if (!mapping || !mapping.project) {
854
- // CC session_id doesn't match any registered MCP client. The MCP
855
- // server uses a different UUID than CC's session_id. Match to a
856
- // session with pendingMapping (most recent first).
864
+ if (mapping && !mapping.project) {
865
+ // Known CC session ID but deliberately disconnected (project cleared
866
+ // by /disconnect). Hooks still fire after disconnect — do NOT
867
+ // alias-claim a pending session; the shell intentionally left.
868
+ return json({ status: "not_connected" });
869
+ }
870
+ if (!mapping) {
871
+ // Truly unknown CC session_id — the MCP client's UUID differs from
872
+ // CC's hook session_id. Match to a session with pendingMapping
873
+ // (most recent first) to learn the alias.
857
874
  const pending = Array.from(sessions.values()).filter((m) => m.project && m.pendingMapping);
858
875
  if (pending.length > 0) {
859
876
  // Map the CC session ID to the most recently connected pending session
@@ -1042,6 +1059,13 @@ export function startDaemon(port = Number(process.env.POLARIS_DAEMON_PORT ?? 432
1042
1059
  }
1043
1060
  }
1044
1061
  }
1062
+ // Include seconds since last delivered event so the status line can show
1063
+ // a liveness indicator (e.g. "· 2m ago") without a second request.
1064
+ let lastEventAge: number | null = null;
1065
+ const cp = await readCheckpoint(ccSessionId);
1066
+ if (cp && cp.project === mapping.project && cp.session === mapping.session) {
1067
+ lastEventAge = Math.floor((Date.now() - new Date(cp.lastEventTimestamp).getTime()) / 1000);
1068
+ }
1045
1069
  return json({
1046
1070
  connected: true,
1047
1071
  project: mapping.project,
@@ -1049,6 +1073,7 @@ export function startDaemon(port = Number(process.env.POLARIS_DAEMON_PORT ?? 432
1049
1073
  user: mapping.user,
1050
1074
  slackChannel,
1051
1075
  account: emailFromToken(await getTokenForProfile(mapping.profile)),
1076
+ lastEventAge,
1052
1077
  });
1053
1078
  }
1054
1079