All files / core/telemetry squad-telemetry.service.ts

94.28% Statements 33/35
73.33% Branches 11/15
100% Functions 6/6
93.93% Lines 31/33

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 1104x 4x 4x 4x 4x 4x 4x     4x     106x   106x   106x               58x   58x 58x       58x 57x               1x 1x     1x         58x 58x             58x           58x 58x 58x                 57x                 57x 57x                   48x 48x       106x                    
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { SessionEntity } from '../../nest/entities/session.entity';
import { SquadEntity } from '../../nest/entities/squad.entity';
import { AgentEntity } from '../../nest/entities/agent.entity';
import * as crypto from 'crypto';
 
@Injectable()
export class SquadTelemetryService {
  constructor(
    @InjectRepository(SessionEntity)
    private readonly sessionRepo: Repository<SessionEntity>,
    @InjectRepository(SquadEntity)
    private readonly squadRepo: Repository<SquadEntity>,
    @InjectRepository(AgentEntity)
    private readonly agentRepo: Repository<AgentEntity>
  ) {}
 
  async ensureSession(args: {
    orchestratorChatId?: string;
    workspaceId?: string;
  }): Promise<string> {
    const originatorId =
      args.orchestratorChatId ?? args.workspaceId ?? process.cwd();
 
    const nowIso = new Date().toISOString();
    let session = await this.sessionRepo.findOne({
      where: { originatorId }
    });
 
    if (!session) {
      session = this.sessionRepo.create({
        originatorId,
        orchestratorChatId: args.orchestratorChatId,
        workspaceId: args.workspaceId ?? process.cwd(),
        createdAt: nowIso,
        lastActivityAt: nowIso
      });
    } else {
      session.lastActivityAt = nowIso;
      Iif (args.orchestratorChatId && !session.orchestratorChatId) {
        session.orchestratorChatId = args.orchestratorChatId;
      }
      Iif (args.workspaceId && !session.workspaceId) {
        session.workspaceId = args.workspaceId;
      }
    }
 
    await this.sessionRepo.save(session);
    return originatorId;
  }
 
  async createSquad(
    originatorId: string,
    label: string
  ): Promise<SquadEntity> {
    const squad = this.squadRepo.create({
      squadId: crypto.randomUUID(),
      originatorId,
      createdAt: new Date().toISOString(),
      label
    });
    await this.squadRepo.save(squad);
    await this.touchSession(originatorId);
    return squad;
  }
 
  async createAgent(
    squadId: string,
    roleName: string,
    task: string | undefined,
    prompt: string | undefined
  ): Promise<AgentEntity> {
    const agent = this.agentRepo.create({
      agentId: crypto.randomUUID(),
      squadId,
      roleName,
      task,
      prompt,
      status: 'starting',
      startedAt: new Date().toISOString()
    });
    await this.agentRepo.save(agent);
    return agent;
  }
 
  async updateAgentStatus(
    originatorId: string,
    agentId: string,
    patch: Partial<
      Pick<AgentEntity, 'status' | 'result' | 'error' | 'finishedAt'>
    >
  ): Promise<void> {
    await this.agentRepo.update(agentId, patch);
    await this.touchSession(originatorId);
  }
 
  private async touchSession(originatorId: string): Promise<void> {
    await this.sessionRepo.update(originatorId, {
      lastActivityAt: new Date().toISOString()
    });
  }
}