(ctx context.Context)
| 1778 | } |
| 1779 | |
| 1780 | func (cli *ChatCLI) cleanup(ctx context.Context) { |
| 1781 | // Teardown should not be aborted if the caller's context was already |
| 1782 | // cancelled (e.g. Ctrl+C); detach cancellation but inherit values. |
| 1783 | ctx = context.WithoutCancel(ctx) |
| 1784 | |
| 1785 | // Close the on-disk hub opened by local mode, if any. |
| 1786 | if cli.hubLocalClose != nil { |
| 1787 | cli.hubLocalClose() |
| 1788 | cli.hubLocalClose = nil |
| 1789 | } |
| 1790 | |
| 1791 | // Fire SessionEnd hook |
| 1792 | if cli.hookManager != nil { |
| 1793 | wd, _ := os.Getwd() |
| 1794 | cli.hookManager.Fire(ctx, hooks.HookEvent{ |
| 1795 | Type: hooks.EventSessionEnd, |
| 1796 | Timestamp: time.Now(), |
| 1797 | SessionID: cli.currentSessionName, |
| 1798 | WorkingDir: wd, |
| 1799 | }) |
| 1800 | } |
| 1801 | |
| 1802 | // Record session end for usage pattern tracking |
| 1803 | if cli.memoryStore != nil && !cli.sessionStartTime.IsZero() { |
| 1804 | if mgr := cli.memoryStore.Manager(); mgr != nil { |
| 1805 | mgr.Patterns.RecordSessionEnd(time.Since(cli.sessionStartTime)) |
| 1806 | } |
| 1807 | } |
| 1808 | |
| 1809 | // Stop background memory worker |
| 1810 | if cli.memWorker != nil { |
| 1811 | cli.memWorker.stop() |
| 1812 | } |
| 1813 | |
| 1814 | // Drain and shut down the durable reflexion queue. Pending jobs |
| 1815 | // remain in the WAL and will be replayed on the next boot. |
| 1816 | cli.reflexionRunnerMu.Lock() |
| 1817 | rnr := cli.reflexionRunner |
| 1818 | cli.reflexionRunnerMu.Unlock() |
| 1819 | if rnr != nil { |
| 1820 | rnr.DrainAndShutdown(30 * time.Second) |
| 1821 | } |
| 1822 | |
| 1823 | // Drain and shut down the scheduler subsystem. |
| 1824 | cli.shutdownScheduler() |
| 1825 | |
| 1826 | // Stop MCP servers (and the hot-reload watcher first so it can't |
| 1827 | // fire a Reload mid-shutdown). Bound the whole MCP teardown at |
| 1828 | // 5s so a stuck transport cannot stall the CLI's exit path. |
| 1829 | cli.stopMCPConfigWatcher() |
| 1830 | cli.shutdownChannelTriggers() |
| 1831 | if cli.mcpManager != nil { |
| 1832 | stopCtx, cancelStop := context.WithTimeout(ctx, 5*time.Second) |
| 1833 | cli.mcpManager.StopAll(stopCtx) |
| 1834 | _ = cli.mcpManager.CloseChannels() |
| 1835 | cancelStop() |
| 1836 | } |
| 1837 | if cli.mcpCancel != nil { |
no test coverage detected