(id: string, options: StopSessionOptions = {})
| 500 | }); |
| 501 | |
| 502 | const msgStore = sessionMsgStoreManager.get(id); |
| 503 | if (msgStore) { |
| 504 | msgStore.pushFinished(); |
| 505 | try { |
| 506 | await this.flushSnapshotPersist(id, SessionStatus.CANCELLED); |
| 507 | } catch (error) { |
| 508 | console.error(`[SessionManager] Failed to persist cancelled snapshot for ${id}:`, error); |
| 509 | await prisma.session.update({ |
| 510 | where: { id }, |
| 511 | data: { status: SessionStatus.CANCELLED }, |
| 512 | }); |
| 513 | } |
| 514 | } else { |
| 515 | await prisma.session.update({ |
| 516 | where: { id }, |
| 517 | data: { status: SessionStatus.CANCELLED }, |
| 518 | }); |
| 519 | } |
| 520 | |
| 521 | if (!options.skipTeamRunReconcile && !this.isConversationSession(session)) { |
| 522 | await this.teamReconciler.handleSessionStopped(id); |
| 523 | } |
| 524 | this.eventBus.emit('session:stopped', { sessionId: id }); |
| 525 | // Cancellation does not run normal terminal finalization, so release the |
| 526 | // store after the CANCELLED snapshot has been persisted above. |
| 527 | sessionMsgStoreManager.delete(id); |
| 528 | this.releaseSnapshotPersistenceState(id); |
| 529 | revokeAgentApiCredential(id); |
| 530 | return session; |
| 531 | } |
| 532 | |
| 533 | /** @deprecated Use hasActiveTurn(). */ |
| 534 | hasActivePipeline(sessionId: string): boolean { |
| 535 | return this.runtimeCoordinator.hasActiveTurn(sessionId); |
| 536 | } |
| 537 | |
| 538 | hasActiveTurn(sessionId: string): boolean { |
| 539 | return this.runtimeCoordinator.hasActiveTurn(sessionId); |
| 540 | } |
| 541 | |
| 542 | isAwaitingPermission(sessionId: string): boolean { |
| 543 | return this.runtimeCoordinator.isAwaitingPermission(sessionId); |
| 544 | } |
| 545 | |
| 546 | /** |
| 547 | * 节流写入 TeamRun invocation 的心跳时间戳。非 TeamRun session 无对应 invocation,updateMany 命中 0 行无副作用。 |
| 548 | */ |
| 549 | private maybeRecordTeamRunHeartbeat(sessionId: string, patch: unknown): void { |
| 550 | // 过滤掉本地 user_message patch(含心跳唤醒注入的消息),只让 agent 真实进展刷新心跳。 |
| 551 | if (!isAgentProgressPatch(patch)) { |
| 552 | return; |
| 553 | } |
| 554 | const now = Date.now(); |
| 555 | const last = this.heartbeatThrottle.get(sessionId) ?? 0; |
| 556 | if (now - last < SessionManager.HEARTBEAT_THROTTLE_MS) { |
| 557 | return; |
| 558 | } |
| 559 | this.heartbeatThrottle.set(sessionId, now); |
no test coverage detected