@aexol/spectral 0.1.8 → 0.1.9

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.
@@ -177,8 +177,15 @@ function dispatchRoute(match, body, deps) {
177
177
  const { store, manager, publishMetaEvent, logger } = deps;
178
178
  const id = match.id ?? "";
179
179
  switch (match.route) {
180
- case "list_projects":
181
- return handleListProjects(store);
180
+ case "list_projects": {
181
+ const activeIds = manager.getActiveTurnSessionIds();
182
+ const projects = handleListProjects(store);
183
+ return projects.map((p) => {
184
+ const sessions = store.listSessionsByProject(p.id);
185
+ const hasActiveSession = sessions.some((s) => activeIds.has(s.id));
186
+ return { ...p, hasActiveSession };
187
+ });
188
+ }
182
189
  case "create_project": {
183
190
  const project = handleCreateProject(store, asObject(body));
184
191
  safePublish(publishMetaEvent, logger, {
@@ -214,8 +221,14 @@ function dispatchRoute(match, body, deps) {
214
221
  });
215
222
  return result;
216
223
  }
217
- case "list_project_sessions":
218
- return handleListSessionsByProject(store, id);
224
+ case "list_project_sessions": {
225
+ const activeIds = manager.getActiveTurnSessionIds();
226
+ const sessions = handleListSessionsByProject(store, id);
227
+ return sessions.map((s) => ({
228
+ ...s,
229
+ hasActiveTurn: activeIds.has(s.id),
230
+ }));
231
+ }
219
232
  case "create_session": {
220
233
  const session = handleCreateSession(store, asObject(body));
221
234
  safePublish(publishMetaEvent, logger, {
@@ -117,6 +117,20 @@ export class SessionStreamManager {
117
117
  hasActiveTurn(sessionId) {
118
118
  return this.streams.get(sessionId)?.currentTurn != null;
119
119
  }
120
+ /**
121
+ * Return the set of session IDs that currently have an in-flight turn.
122
+ * Cheap O(streams) scan used by the list-project / list-sessions endpoints
123
+ * to enrich responses with `hasActiveTurn` markers so the frontend can
124
+ * render a yellow pulsating dot next to active sessions/projects.
125
+ */
126
+ getActiveTurnSessionIds() {
127
+ const ids = new Set();
128
+ for (const [sid, stream] of this.streams) {
129
+ if (stream.currentTurn !== null)
130
+ ids.add(sid);
131
+ }
132
+ return ids;
133
+ }
120
134
  /**
121
135
  * Persist a user message and forward it to pi. Resolves after the user
122
136
  * message is persisted + pi is invoked (NOT after the turn completes —
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aexol/spectral",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "Always-on coding agent for Aexol — branded pi wrapper with relay-based browser access.",
5
5
  "type": "module",
6
6
  "private": false,