claudia-orchestrator 0.1.5 → 0.1.7
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.
- package/README.md +22 -77
- package/dist/cli-parser.d.ts +0 -2
- package/dist/cli-parser.d.ts.map +1 -1
- package/dist/cli-parser.js +1 -13
- package/dist/cli-parser.js.map +1 -1
- package/dist/cui-server.d.ts +1 -3
- package/dist/cui-server.d.ts.map +1 -1
- package/dist/cui-server.js +175 -63
- package/dist/cui-server.js.map +1 -1
- package/dist/process-daemon/index.js +2 -2
- package/dist/process-daemon/index.js.map +1 -1
- package/dist/process-daemon/process-daemon.d.ts.map +1 -1
- package/dist/process-daemon/process-daemon.js +27 -9
- package/dist/process-daemon/process-daemon.js.map +1 -1
- package/dist/process-daemon/process-manager-client.d.ts +14 -3
- package/dist/process-daemon/process-manager-client.d.ts.map +1 -1
- package/dist/process-daemon/process-manager-client.js +6 -4
- package/dist/process-daemon/process-manager-client.js.map +1 -1
- package/dist/process-daemon/process-manager-interface.d.ts +16 -2
- package/dist/process-daemon/process-manager-interface.d.ts.map +1 -1
- package/dist/process-daemon/test-daemon.js +24 -24
- package/dist/process-daemon/test-daemon.js.map +1 -1
- package/dist/process-daemon/types.d.ts +13 -1
- package/dist/process-daemon/types.d.ts.map +1 -1
- package/dist/process-daemon/types.js.map +1 -1
- package/dist/routes/analysis.routes.d.ts +13 -0
- package/dist/routes/analysis.routes.d.ts.map +1 -0
- package/dist/routes/analysis.routes.js +103 -0
- package/dist/routes/analysis.routes.js.map +1 -0
- package/dist/routes/conversation.routes.d.ts +1 -1
- package/dist/routes/conversation.routes.d.ts.map +1 -1
- package/dist/routes/conversation.routes.js +76 -27
- package/dist/routes/conversation.routes.js.map +1 -1
- package/dist/routes/insights.routes.d.ts.map +1 -1
- package/dist/routes/insights.routes.js +371 -29
- package/dist/routes/insights.routes.js.map +1 -1
- package/dist/routes/license.routes.d.ts.map +1 -1
- package/dist/routes/license.routes.js +0 -16
- package/dist/routes/license.routes.js.map +1 -1
- package/dist/routes/log.routes.d.ts.map +1 -1
- package/dist/routes/log.routes.js +52 -0
- package/dist/routes/log.routes.js.map +1 -1
- package/dist/routes/pending-question.routes.d.ts +9 -0
- package/dist/routes/pending-question.routes.d.ts.map +1 -0
- package/dist/routes/pending-question.routes.js +154 -0
- package/dist/routes/pending-question.routes.js.map +1 -0
- package/dist/routes/process-events.routes.d.ts +8 -0
- package/dist/routes/process-events.routes.d.ts.map +1 -0
- package/dist/routes/process-events.routes.js +141 -0
- package/dist/routes/process-events.routes.js.map +1 -0
- package/dist/routes/question.routes.d.ts +1 -1
- package/dist/routes/question.routes.d.ts.map +1 -1
- package/dist/routes/question.routes.js +10 -9
- package/dist/routes/question.routes.js.map +1 -1
- package/dist/routes/streaming.routes.d.ts.map +1 -1
- package/dist/routes/streaming.routes.js +3 -7
- package/dist/routes/streaming.routes.js.map +1 -1
- package/dist/services/anthropic-service.d.ts +44 -6
- package/dist/services/anthropic-service.d.ts.map +1 -1
- package/dist/services/anthropic-service.js +278 -92
- package/dist/services/anthropic-service.js.map +1 -1
- package/dist/services/claude-history-reader.d.ts +11 -0
- package/dist/services/claude-history-reader.d.ts.map +1 -1
- package/dist/services/claude-history-reader.js +105 -6
- package/dist/services/claude-history-reader.js.map +1 -1
- package/dist/services/claude-process-manager.d.ts +24 -4
- package/dist/services/claude-process-manager.d.ts.map +1 -1
- package/dist/services/claude-process-manager.js +137 -9
- package/dist/services/claude-process-manager.js.map +1 -1
- package/dist/services/claudia-service.d.ts.map +1 -1
- package/dist/services/claudia-service.js.map +1 -1
- package/dist/services/config-service.d.ts.map +1 -1
- package/dist/services/config-service.js +3 -18
- package/dist/services/config-service.js.map +1 -1
- package/dist/services/conversation-status-manager.d.ts +12 -0
- package/dist/services/conversation-status-manager.d.ts.map +1 -1
- package/dist/services/conversation-status-manager.js +38 -4
- package/dist/services/conversation-status-manager.js.map +1 -1
- package/dist/services/cost-tracker.d.ts +27 -2
- package/dist/services/cost-tracker.d.ts.map +1 -1
- package/dist/services/cost-tracker.js +101 -11
- package/dist/services/cost-tracker.js.map +1 -1
- package/dist/services/debug-log.d.ts +6 -0
- package/dist/services/debug-log.d.ts.map +1 -0
- package/dist/services/debug-log.js +27 -0
- package/dist/services/debug-log.js.map +1 -0
- package/dist/services/gemini-service.d.ts +0 -22
- package/dist/services/gemini-service.d.ts.map +1 -1
- package/dist/services/gemini-service.js +8 -137
- package/dist/services/gemini-service.js.map +1 -1
- package/dist/services/insight-queue.js +3 -3
- package/dist/services/insight-queue.js.map +1 -1
- package/dist/services/insights-event-log.d.ts +196 -0
- package/dist/services/insights-event-log.d.ts.map +1 -0
- package/dist/services/insights-event-log.js +319 -0
- package/dist/services/insights-event-log.js.map +1 -0
- package/dist/services/insights-service.d.ts +49 -0
- package/dist/services/insights-service.d.ts.map +1 -1
- package/dist/services/insights-service.js +655 -100
- package/dist/services/insights-service.js.map +1 -1
- package/dist/services/license-service.d.ts.map +1 -1
- package/dist/services/license-service.js.map +1 -1
- package/dist/services/pending-question-service.d.ts +97 -0
- package/dist/services/pending-question-service.d.ts.map +1 -0
- package/dist/services/pending-question-service.js +223 -0
- package/dist/services/pending-question-service.js.map +1 -0
- package/dist/services/process-event-log.d.ts +263 -0
- package/dist/services/process-event-log.d.ts.map +1 -0
- package/dist/services/process-event-log.js +509 -0
- package/dist/services/process-event-log.js.map +1 -0
- package/dist/services/process-manager-factory.d.ts +19 -1
- package/dist/services/process-manager-factory.d.ts.map +1 -1
- package/dist/services/process-manager-factory.js +88 -7
- package/dist/services/process-manager-factory.js.map +1 -1
- package/dist/services/question-tracker.d.ts +4 -0
- package/dist/services/question-tracker.d.ts.map +1 -1
- package/dist/services/question-tracker.js +6 -0
- package/dist/services/question-tracker.js.map +1 -1
- package/dist/services/session-activity-watcher.d.ts +1 -1
- package/dist/services/session-activity-watcher.d.ts.map +1 -1
- package/dist/services/session-activity-watcher.js +10 -7
- package/dist/services/session-activity-watcher.js.map +1 -1
- package/dist/services/session-analysis-service.d.ts +72 -0
- package/dist/services/session-analysis-service.d.ts.map +1 -0
- package/dist/services/session-analysis-service.js +373 -0
- package/dist/services/session-analysis-service.js.map +1 -0
- package/dist/services/session-info-service.d.ts +23 -0
- package/dist/services/session-info-service.d.ts.map +1 -1
- package/dist/services/session-info-service.js +46 -10
- package/dist/services/session-info-service.js.map +1 -1
- package/dist/services/session-insights-service.d.ts.map +1 -1
- package/dist/services/session-insights-service.js +96 -19
- package/dist/services/session-insights-service.js.map +1 -1
- package/dist/services/stream-manager.d.ts.map +1 -1
- package/dist/services/stream-manager.js +9 -2
- package/dist/services/stream-manager.js.map +1 -1
- package/dist/types/config.d.ts +1 -6
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js.map +1 -1
- package/dist/types/index.d.ts +12 -19
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/insights.d.ts +44 -2
- package/dist/types/insights.d.ts.map +1 -1
- package/dist/utils/server-startup.d.ts +1 -3
- package/dist/utils/server-startup.d.ts.map +1 -1
- package/dist/utils/server-startup.js +3 -14
- package/dist/utils/server-startup.js.map +1 -1
- package/dist/web/assets/main-CFG6c-xC.css +1 -0
- package/dist/web/assets/main-DoNPc_qs.js +484 -0
- package/dist/web/index.html +2 -2
- package/package.json +14 -5
- package/scripts/postinstall.js +27 -3
- package/dist/middleware/auth.d.ts +0 -18
- package/dist/middleware/auth.d.ts.map +0 -1
- package/dist/middleware/auth.js +0 -136
- package/dist/middleware/auth.js.map +0 -1
- package/dist/routes/gemini.routes.d.ts +0 -4
- package/dist/routes/gemini.routes.d.ts.map +0 -1
- package/dist/routes/gemini.routes.js +0 -93
- package/dist/routes/gemini.routes.js.map +0 -1
- package/dist/routes/notifications.routes.d.ts +0 -4
- package/dist/routes/notifications.routes.d.ts.map +0 -1
- package/dist/routes/notifications.routes.js +0 -71
- package/dist/routes/notifications.routes.js.map +0 -1
- package/dist/web/assets/main-CgZfAwiY.js +0 -269
- package/dist/web/assets/main-wtQycIPx.css +0 -1
package/README.md
CHANGED
|
@@ -1,107 +1,52 @@
|
|
|
1
|
-
# Claudia
|
|
1
|
+
# Claudia
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A dashboard for orchestrating Claude Code sessions. Run multiple AI coding sessions in parallel with real-time streaming and AI-powered insights.
|
|
4
4
|
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
Claudia Orchestrator wraps Claude Code CLI sessions and adds:
|
|
8
|
-
|
|
9
|
-
- **Live insight surfacing** — See what the model is doing: assumptions, uncertainties, scope drift, progress
|
|
10
|
-
- **Multi-session management** — Run multiple Claude Code sessions in parallel from one dashboard
|
|
11
|
-
- **Real-time streaming** — PTY-based process management with instant output
|
|
12
|
-
- **Session persistence** — Sessions survive server restarts
|
|
13
|
-
|
|
14
|
-
## Quick Start
|
|
15
|
-
|
|
16
|
-
**Requirements:** Node.js 22+, Claude Code CLI installed and authenticated
|
|
5
|
+
## Install
|
|
17
6
|
|
|
18
7
|
```bash
|
|
19
|
-
# Run directly (no install needed)
|
|
20
8
|
npx claudia-orchestrator
|
|
9
|
+
```
|
|
21
10
|
|
|
22
|
-
|
|
11
|
+
Or install globally:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
23
14
|
npm install -g claudia-orchestrator
|
|
24
15
|
claudia
|
|
25
16
|
```
|
|
26
17
|
|
|
27
|
-
|
|
18
|
+
**Requirements:** Node.js 22+, Claude Code CLI installed and authenticated
|
|
28
19
|
|
|
29
20
|
## Features
|
|
30
21
|
|
|
31
|
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
-
|
|
35
|
-
-
|
|
36
|
-
|
|
37
|
-
### AI Insights (Beta)
|
|
38
|
-
During beta, all users get access to AI-powered session insights:
|
|
39
|
-
- **Current State** — What's happening right now
|
|
40
|
-
- **Milestones** — Progress tracking with done/current/pending tasks
|
|
41
|
-
- **Notable Events** — Errors, breakthroughs, decisions, course corrections
|
|
42
|
-
- **Context Panels** — Key information extracted from the session
|
|
43
|
-
|
|
44
|
-
Insights require your own Anthropic API key (set `ANTHROPIC_API_KEY` environment variable).
|
|
45
|
-
|
|
46
|
-
### Voice Input
|
|
47
|
-
Dictation powered by Gemini 2.5 Flash. Requires a Google API key (`GOOGLE_API_KEY`).
|
|
48
|
-
|
|
49
|
-
### Push Notifications
|
|
50
|
-
Get notified when sessions complete or need input. Supports web-push and ntfy.sh.
|
|
22
|
+
- **Multi-session management** — Run multiple Claude Code sessions in parallel
|
|
23
|
+
- **Real-time streaming** — PTY-based output with instant feedback
|
|
24
|
+
- **AI insights** — See what the model is doing: assumptions, progress, uncertainties
|
|
25
|
+
- **Session persistence** — Sessions survive server restarts
|
|
26
|
+
- **Voice input** — Dictation powered by Gemini 2.5 Flash
|
|
27
|
+
- **Push notifications** — Get notified when sessions complete or need input
|
|
51
28
|
|
|
52
|
-
##
|
|
29
|
+
## Setup
|
|
53
30
|
|
|
54
|
-
|
|
55
|
-
- `config.json` — Server settings, API keys, auth token
|
|
56
|
-
- `session-info.db` — Session metadata and insights cache
|
|
31
|
+
The dashboard runs at `http://localhost:3001`. Your auth token is displayed on first launch.
|
|
57
32
|
|
|
58
|
-
###
|
|
33
|
+
### API Keys
|
|
59
34
|
|
|
60
35
|
| Variable | Purpose |
|
|
61
36
|
|----------|---------|
|
|
62
37
|
| `ANTHROPIC_API_KEY` | Required for AI insights |
|
|
63
38
|
| `GOOGLE_API_KEY` | Optional for voice dictation |
|
|
64
39
|
|
|
40
|
+
Set these in your environment or in `~/.claudia/config.json`.
|
|
41
|
+
|
|
65
42
|
### Remote Access
|
|
66
43
|
|
|
67
44
|
1. Edit `~/.claudia/config.json`, set `server.host` to `0.0.0.0`
|
|
68
|
-
2. Use HTTPS via reverse proxy (Caddy, nginx)
|
|
69
|
-
3. Or use Tailscale Serve for secure access
|
|
70
|
-
|
|
71
|
-
## System Requirements
|
|
72
|
-
|
|
73
|
-
- **Node.js:** 22+ (native modules: node-pty, better-sqlite3, sharp)
|
|
74
|
-
- **Claude Code:** Must be installed and authenticated (`claude` command available)
|
|
75
|
-
- **Platform:** Linux, macOS, WSL2
|
|
76
|
-
|
|
77
|
-
## Beta Status
|
|
78
|
-
|
|
79
|
-
This is beta software. During beta:
|
|
80
|
-
- All Pro features are enabled for everyone
|
|
81
|
-
- AI insights require your own API key
|
|
82
|
-
- Session limits are not enforced
|
|
83
|
-
|
|
84
|
-
## Troubleshooting
|
|
85
|
-
|
|
86
|
-
### "Module not found" or native module errors
|
|
87
|
-
```bash
|
|
88
|
-
# Rebuild native modules for your Node version
|
|
89
|
-
rm -rf node_modules
|
|
90
|
-
npm install
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
### Sessions not starting
|
|
94
|
-
Ensure Claude Code CLI is working:
|
|
95
|
-
```bash
|
|
96
|
-
claude --version
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
### Insights not generating
|
|
100
|
-
Check that `ANTHROPIC_API_KEY` is set in `~/.claudia/config.json` or as an environment variable.
|
|
45
|
+
2. Use HTTPS via reverse proxy (Caddy, nginx) or Tailscale Serve
|
|
101
46
|
|
|
102
|
-
##
|
|
47
|
+
## Status
|
|
103
48
|
|
|
104
|
-
|
|
49
|
+
Claudia is in beta. For feedback or support, email jason@liggi.dev.
|
|
105
50
|
|
|
106
51
|
## License
|
|
107
52
|
|
package/dist/cli-parser.d.ts
CHANGED
package/dist/cli-parser.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli-parser.d.ts","sourceRoot":"","sources":["../src/cli-parser.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,SAAS;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"cli-parser.d.ts","sourceRoot":"","sources":["../src/cli-parser.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,SAAS;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,SAAS,CAyCnD"}
|
package/dist/cli-parser.js
CHANGED
|
@@ -34,21 +34,9 @@ export function parseArgs(argv) {
|
|
|
34
34
|
process.exit(1);
|
|
35
35
|
}
|
|
36
36
|
break;
|
|
37
|
-
case '--token':
|
|
38
|
-
if (i + 1 < args.length) {
|
|
39
|
-
config.token = args[++i];
|
|
40
|
-
}
|
|
41
|
-
else {
|
|
42
|
-
logger.error('--token requires a value');
|
|
43
|
-
process.exit(1);
|
|
44
|
-
}
|
|
45
|
-
break;
|
|
46
|
-
case '--skip-auth-token':
|
|
47
|
-
config.skipAuthToken = true;
|
|
48
|
-
break;
|
|
49
37
|
default:
|
|
50
38
|
logger.error(`Unknown argument: ${arg}`);
|
|
51
|
-
logger.info('Usage: claudia [--port <number>] [--host <string>]
|
|
39
|
+
logger.info('Usage: claudia [--port <number>] [--host <string>]');
|
|
52
40
|
process.exit(1);
|
|
53
41
|
}
|
|
54
42
|
}
|
package/dist/cli-parser.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli-parser.js","sourceRoot":"","sources":["../src/cli-parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"cli-parser.js","sourceRoot":"","sources":["../src/cli-parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAOpD;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,IAAc;IACtC,MAAM,MAAM,GAAG,YAAY,CAAC,WAAW,CAAC,CAAC;IACzC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC3B,MAAM,MAAM,GAAc,EAAE,CAAC;IAE7B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QAEpB,QAAQ,GAAG,EAAE,CAAC;YACZ,KAAK,QAAQ;gBACX,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;oBACxB,MAAM,SAAS,GAAG,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;oBAC1C,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,SAAS,GAAG,CAAC,IAAI,SAAS,IAAI,KAAK,EAAE,CAAC;wBAC7D,MAAM,CAAC,IAAI,GAAG,SAAS,CAAC;oBAC1B,CAAC;yBAAM,CAAC;wBACN,MAAM,CAAC,KAAK,CAAC,uBAAuB,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;wBAC/C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;oBAClB,CAAC;gBACH,CAAC;qBAAM,CAAC;oBACN,MAAM,CAAC,KAAK,CAAC,yBAAyB,CAAC,CAAC;oBACxC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBAClB,CAAC;gBACD,MAAM;YAER,KAAK,QAAQ;gBACX,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;oBACxB,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;gBAC1B,CAAC;qBAAM,CAAC;oBACN,MAAM,CAAC,KAAK,CAAC,yBAAyB,CAAC,CAAC;oBACxC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBAClB,CAAC;gBACD,MAAM;YAER;gBACE,MAAM,CAAC,KAAK,CAAC,qBAAqB,GAAG,EAAE,CAAC,CAAC;gBACzC,MAAM,CAAC,IAAI,CAAC,oDAAoD,CAAC,CAAC;gBAClE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
package/dist/cui-server.d.ts
CHANGED
|
@@ -8,9 +8,9 @@ export declare class CUIServer {
|
|
|
8
8
|
private processManager;
|
|
9
9
|
private streamManager;
|
|
10
10
|
private historyReader;
|
|
11
|
-
private statusTracker;
|
|
12
11
|
private permissionTracker;
|
|
13
12
|
private questionTracker;
|
|
13
|
+
private pendingQuestionService;
|
|
14
14
|
private mcpConfigGenerator;
|
|
15
15
|
private fileSystemService;
|
|
16
16
|
private configService;
|
|
@@ -28,8 +28,6 @@ export declare class CUIServer {
|
|
|
28
28
|
constructor(configOverrides?: {
|
|
29
29
|
port?: number;
|
|
30
30
|
host?: string;
|
|
31
|
-
token?: string;
|
|
32
|
-
skipAuthToken?: boolean;
|
|
33
31
|
});
|
|
34
32
|
/**
|
|
35
33
|
* Get the Express app instance
|
package/dist/cui-server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cui-server.d.ts","sourceRoot":"","sources":["../src/cui-server.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"cui-server.d.ts","sourceRoot":"","sources":["../src/cui-server.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AA0D3C;;GAEG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,GAAG,CAAU;IACrB,OAAO,CAAC,MAAM,CAAC,CAAwB;IACvC,OAAO,CAAC,cAAc,CAAyB;IAC/C,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,aAAa,CAAsB;IAC3C,OAAO,CAAC,iBAAiB,CAAoB;IAC7C,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,sBAAsB,CAAyB;IACvD,OAAO,CAAC,kBAAkB,CAAqB;IAC/C,OAAO,CAAC,iBAAiB,CAAoB;IAC7C,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,kBAAkB,CAAqB;IAC/C,OAAO,CAAC,yBAAyB,CAA4B;IAC7D,OAAO,CAAC,yBAAyB,CAA4B;IAC7D,OAAO,CAAC,kBAAkB,CAAqB;IAC/C,OAAO,CAAC,mBAAmB,CAAsB;IACjD,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,aAAa,CAAC,CAAsB;IAC5C,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,eAAe,CAAC,CAAmC;gBAE/C,eAAe,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE;IAwD9D;;OAEG;IACH,MAAM,IAAI,OAAO;IAIjB;;OAEG;IACH,OAAO,IAAI,MAAM;IAIjB;;OAEG;IACH,OAAO,IAAI,MAAM;IAIjB;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAoIjC;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAsG5B;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAwE3B;;OAEG;YACW,OAAO;IA6BrB,OAAO,CAAC,eAAe;IAwBvB,OAAO,CAAC,WAAW;IAgEnB,OAAO,CAAC,8BAA8B;IAsQtC,OAAO,CAAC,iCAAiC;IA6BzC,OAAO,CAAC,+BAA+B;YA8BzB,wBAAwB;CAgCvC"}
|
package/dist/cui-server.js
CHANGED
|
@@ -11,6 +11,7 @@ import { StreamManager } from './services/stream-manager.js';
|
|
|
11
11
|
import { ClaudeHistoryReader } from './services/claude-history-reader.js';
|
|
12
12
|
import { PermissionTracker } from './services/permission-tracker.js';
|
|
13
13
|
import { QuestionTracker } from './services/question-tracker.js';
|
|
14
|
+
import { PendingQuestionService } from './services/pending-question-service.js';
|
|
14
15
|
import { MCPConfigGenerator } from './services/mcp-config-generator.js';
|
|
15
16
|
import { FileSystemService } from './services/file-system-service.js';
|
|
16
17
|
import { ConfigService } from './services/config-service.js';
|
|
@@ -22,7 +23,6 @@ import { NotificationService } from './services/notification-service.js';
|
|
|
22
23
|
import { WebPushService } from './services/web-push-service.js';
|
|
23
24
|
import { geminiService } from './services/gemini-service.js';
|
|
24
25
|
import { anthropicService } from './services/anthropic-service.js';
|
|
25
|
-
import { SessionInsightsService } from './services/session-insights-service.js';
|
|
26
26
|
import { ClaudeRouterService } from './services/claude-router-service.js';
|
|
27
27
|
import { CUIError } from './types/index.js';
|
|
28
28
|
import { createLogger } from './services/logger.js';
|
|
@@ -30,22 +30,22 @@ import { createConversationRoutes } from './routes/conversation.routes.js';
|
|
|
30
30
|
import { createSystemRoutes } from './routes/system.routes.js';
|
|
31
31
|
import { createPermissionRoutes } from './routes/permission.routes.js';
|
|
32
32
|
import { createQuestionRoutes } from './routes/question.routes.js';
|
|
33
|
+
import { createPendingQuestionRoutes } from './routes/pending-question.routes.js';
|
|
33
34
|
import { createFileSystemRoutes } from './routes/filesystem.routes.js';
|
|
34
35
|
import { createLogRoutes } from './routes/log.routes.js';
|
|
35
36
|
import { createStreamingRoutes } from './routes/streaming.routes.js';
|
|
36
37
|
import { createWorkingDirectoriesRoutes } from './routes/working-directories.routes.js';
|
|
37
38
|
import { createConfigRoutes } from './routes/config.routes.js';
|
|
38
|
-
import { createGeminiRoutes } from './routes/gemini.routes.js';
|
|
39
|
-
import { createNotificationsRoutes } from './routes/notifications.routes.js';
|
|
40
39
|
import { createInsightsRoutes } from './routes/insights.routes.js';
|
|
41
40
|
import { createClaudiaRoutes } from './routes/claudia.routes.js';
|
|
42
41
|
import { ClaudiaService } from './services/claudia-service.js';
|
|
43
42
|
import { createLicenseRoutes } from './routes/license.routes.js';
|
|
43
|
+
import { createAnalysisRoutes } from './routes/analysis.routes.js';
|
|
44
|
+
import processEventsRouter from './routes/process-events.routes.js';
|
|
44
45
|
import { errorHandler } from './middleware/error-handler.js';
|
|
45
46
|
import { requestLogger } from './middleware/request-logger.js';
|
|
46
47
|
import { createCorsMiddleware } from './middleware/cors-setup.js';
|
|
47
48
|
import { queryParser } from './middleware/query-parser.js';
|
|
48
|
-
import { authMiddleware, createAuthMiddleware } from './middleware/auth.js';
|
|
49
49
|
// ViteExpress will be imported dynamically in initialize() if needed
|
|
50
50
|
let ViteExpress;
|
|
51
51
|
/**
|
|
@@ -57,9 +57,9 @@ export class CUIServer {
|
|
|
57
57
|
processManager; // Initialized in initialize()
|
|
58
58
|
streamManager;
|
|
59
59
|
historyReader;
|
|
60
|
-
statusTracker;
|
|
61
60
|
permissionTracker;
|
|
62
61
|
questionTracker;
|
|
62
|
+
pendingQuestionService;
|
|
63
63
|
mcpConfigGenerator;
|
|
64
64
|
fileSystemService;
|
|
65
65
|
configService;
|
|
@@ -94,15 +94,14 @@ export class CUIServer {
|
|
|
94
94
|
// Use singleton to ensure event handlers get the same initialized instance
|
|
95
95
|
this.sessionInfoService = SessionInfoService.getInstance();
|
|
96
96
|
this.historyReader = new ClaudeHistoryReader(this.sessionInfoService);
|
|
97
|
-
// Create a single instance of ConversationStatusManager for both statusTracker and conversationStatusManager
|
|
98
97
|
this.conversationStatusManager = new ConversationStatusManager();
|
|
99
|
-
this.statusTracker = this.conversationStatusManager; // Use the same instance for backward compatibility
|
|
100
98
|
this.toolMetricsService = new ToolMetricsService();
|
|
101
99
|
this.fileSystemService = new FileSystemService();
|
|
102
100
|
// processManager is initialized in initialize() to support daemon mode
|
|
103
101
|
this.streamManager = new StreamManager();
|
|
104
102
|
this.permissionTracker = new PermissionTracker();
|
|
105
103
|
this.questionTracker = new QuestionTracker();
|
|
104
|
+
this.pendingQuestionService = PendingQuestionService.getInstance();
|
|
106
105
|
this.mcpConfigGenerator = new MCPConfigGenerator(this.fileSystemService);
|
|
107
106
|
this.workingDirectoriesService = new WorkingDirectoriesService(this.historyReader, this.logger);
|
|
108
107
|
this.notificationService = new NotificationService();
|
|
@@ -111,14 +110,11 @@ export class CUIServer {
|
|
|
111
110
|
this.permissionTracker.setNotificationService(this.notificationService);
|
|
112
111
|
this.permissionTracker.setConversationStatusManager(this.conversationStatusManager);
|
|
113
112
|
this.permissionTracker.setHistoryReader(this.historyReader);
|
|
114
|
-
//
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
.then(insights => insightsService.cacheInsights(claudeSessionId, insights))
|
|
120
|
-
.catch(err => this.logger.debug('Background insights computation failed', { claudeSessionId, error: err }));
|
|
121
|
-
});
|
|
113
|
+
// NOTE: Session-ended insights computation was removed because it raced with
|
|
114
|
+
// the event-driven InsightsService. The session-ended handler would read the
|
|
115
|
+
// session file before all messages were flushed, caching insights with
|
|
116
|
+
// messageCount=1. The event-driven system (InsightsService) now handles all
|
|
117
|
+
// insight generation via file watching and action counting.
|
|
122
118
|
this.logger.debug('Services initialized (processManager deferred to initialize())');
|
|
123
119
|
this.setupMiddleware();
|
|
124
120
|
// Routes and processManager integration are set up in initialize()
|
|
@@ -155,15 +151,24 @@ export class CUIServer {
|
|
|
155
151
|
this.logger.debug('Initializing session info service');
|
|
156
152
|
await this.sessionInfoService.initialize();
|
|
157
153
|
this.logger.debug('Session info service initialized successfully');
|
|
158
|
-
// Initialize
|
|
159
|
-
this.logger.debug('Initializing
|
|
160
|
-
this.
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
154
|
+
// Initialize pending question service (uses same DB as session info)
|
|
155
|
+
this.logger.debug('Initializing pending question service');
|
|
156
|
+
await this.pendingQuestionService.initialize();
|
|
157
|
+
this.logger.debug('Pending question service initialized successfully');
|
|
158
|
+
// Initialize process manager (skip if already set, e.g. by tests)
|
|
159
|
+
if (!this.processManager) {
|
|
160
|
+
this.logger.debug('Initializing process manager');
|
|
161
|
+
this.processManager = await createProcessManager({
|
|
162
|
+
historyReader: this.historyReader,
|
|
163
|
+
statusTracker: this.conversationStatusManager,
|
|
164
|
+
toolMetricsService: this.toolMetricsService,
|
|
165
|
+
sessionInfoService: this.sessionInfoService,
|
|
166
|
+
fileSystemService: this.fileSystemService
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
this.logger.debug('Process manager already set, skipping initialization');
|
|
171
|
+
}
|
|
167
172
|
this.processManager.setNotificationService(this.notificationService);
|
|
168
173
|
this.processManager.setConversationStatusManager(this.conversationStatusManager);
|
|
169
174
|
// Set up event listeners now that processManager exists
|
|
@@ -184,8 +189,12 @@ export class CUIServer {
|
|
|
184
189
|
this.logger.debug('Cost tracker initialized successfully');
|
|
185
190
|
// Initialize insights service (unified event-driven system)
|
|
186
191
|
this.logger.debug('Initializing insights service');
|
|
187
|
-
const { initializeInsightsService } = await import('./services/insights-service.js');
|
|
192
|
+
const { initializeInsightsService, getInsightsService } = await import('./services/insights-service.js');
|
|
188
193
|
initializeInsightsService();
|
|
194
|
+
// Wire up session-ended to expedite insight patches
|
|
195
|
+
this.conversationStatusManager.on('session-ended', ({ claudeSessionId }) => {
|
|
196
|
+
getInsightsService().handleSessionEnded(claudeSessionId);
|
|
197
|
+
});
|
|
189
198
|
this.logger.debug('Insights service initialized successfully');
|
|
190
199
|
// Initialize router service if configured
|
|
191
200
|
await this.initializeOrReloadRouter(config);
|
|
@@ -277,12 +286,9 @@ export class CUIServer {
|
|
|
277
286
|
address: this.server?.address()
|
|
278
287
|
});
|
|
279
288
|
// Display startup info now that server is actually listening
|
|
280
|
-
const config = this.configService.getConfig();
|
|
281
289
|
displayServerStartup({
|
|
282
290
|
host: this.host,
|
|
283
291
|
port: this.port,
|
|
284
|
-
authToken: this.configOverrides?.token ?? config.authToken,
|
|
285
|
-
skipAuthToken: this.configOverrides?.skipAuthToken,
|
|
286
292
|
logger: this.logger
|
|
287
293
|
});
|
|
288
294
|
// Configure and bind ViteExpress AFTER server is listening
|
|
@@ -310,12 +316,9 @@ export class CUIServer {
|
|
|
310
316
|
mode: process.env.NODE_ENV || 'production'
|
|
311
317
|
});
|
|
312
318
|
// Display startup info now that server is actually listening
|
|
313
|
-
const config = this.configService.getConfig();
|
|
314
319
|
displayServerStartup({
|
|
315
320
|
host: this.host,
|
|
316
321
|
port: this.port,
|
|
317
|
-
authToken: this.configOverrides?.token ?? config.authToken,
|
|
318
|
-
skipAuthToken: this.configOverrides?.skipAuthToken,
|
|
319
322
|
logger: this.logger
|
|
320
323
|
});
|
|
321
324
|
resolve();
|
|
@@ -461,38 +464,21 @@ export class CUIServer {
|
|
|
461
464
|
// System routes (includes health check) - before auth
|
|
462
465
|
this.app.use('/api/system', createSystemRoutes(this.processManager, this.historyReader));
|
|
463
466
|
this.app.use('/', createSystemRoutes(this.processManager, this.historyReader)); // For /health at root
|
|
464
|
-
// Permission routes
|
|
467
|
+
// Permission routes (needed for MCP server communication)
|
|
465
468
|
this.app.use('/api/permissions', createPermissionRoutes(this.permissionTracker));
|
|
466
|
-
// Notifications routes - before auth (needed for service worker subscription on first load)
|
|
467
|
-
this.app.use('/api/notifications', createNotificationsRoutes(this.webPushService));
|
|
468
|
-
// Apply auth middleware to all other API routes unless skipAuthToken is set
|
|
469
|
-
if (!this.configOverrides?.skipAuthToken) {
|
|
470
|
-
if (this.configOverrides?.token) {
|
|
471
|
-
// Use custom auth middleware with token override
|
|
472
|
-
this.app.use('/api', createAuthMiddleware(this.configOverrides.token));
|
|
473
|
-
this.logger.info('Using custom authentication token from CLI');
|
|
474
|
-
}
|
|
475
|
-
else {
|
|
476
|
-
// Use default auth middleware
|
|
477
|
-
this.app.use('/api', authMiddleware);
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
else {
|
|
481
|
-
this.logger.warn('Authentication middleware is disabled - API endpoints are not protected!');
|
|
482
|
-
}
|
|
483
469
|
// API routes
|
|
484
470
|
// IMPORTANT: Insights routes must be registered BEFORE conversation routes
|
|
485
471
|
// because conversation routes have a /:sessionId catch-all pattern that would
|
|
486
472
|
// otherwise match paths like /activity-check
|
|
487
473
|
this.app.use('/api/conversations', createInsightsRoutes(this.historyReader, this.sessionInfoService));
|
|
488
|
-
this.app.use('/api/conversations', createConversationRoutes(this.processManager, this.historyReader, this.
|
|
474
|
+
this.app.use('/api/conversations', createConversationRoutes(this.processManager, this.historyReader, this.conversationStatusManager, this.sessionInfoService, this.toolMetricsService));
|
|
489
475
|
this.app.use('/api/filesystem', createFileSystemRoutes(this.fileSystemService));
|
|
490
476
|
this.app.use('/api/logs', createLogRoutes());
|
|
491
477
|
this.app.use('/api/stream', createStreamingRoutes(this.streamManager));
|
|
492
478
|
this.app.use('/api/working-directories', createWorkingDirectoriesRoutes(this.workingDirectoriesService));
|
|
493
479
|
this.app.use('/api/config', createConfigRoutes(this.configService));
|
|
494
|
-
this.app.use('/api/gemini', createGeminiRoutes(geminiService));
|
|
495
480
|
this.app.use('/api/questions', createQuestionRoutes(this.questionTracker, this.processManager));
|
|
481
|
+
this.app.use('/api/pending-questions', createPendingQuestionRoutes(this.pendingQuestionService, this.processManager, this.historyReader));
|
|
496
482
|
// Claudia orchestrator routes
|
|
497
483
|
const claudiaService = ClaudiaService.getInstance({
|
|
498
484
|
processManager: this.processManager,
|
|
@@ -502,6 +488,10 @@ export class CUIServer {
|
|
|
502
488
|
this.app.use('/api/claudia', createClaudiaRoutes(claudiaService));
|
|
503
489
|
// License routes
|
|
504
490
|
this.app.use('/api/license', createLicenseRoutes());
|
|
491
|
+
// Post-session analysis routes
|
|
492
|
+
this.app.use('/api/analysis', createAnalysisRoutes());
|
|
493
|
+
// Process event log routes (observability)
|
|
494
|
+
this.app.use('/api/process-events', processEventsRouter);
|
|
505
495
|
// React Router catch-all - must be after all API routes
|
|
506
496
|
const isDev = process.env.NODE_ENV === 'development';
|
|
507
497
|
if (!isDev) {
|
|
@@ -518,6 +508,9 @@ export class CUIServer {
|
|
|
518
508
|
this.logger.debug('Setting up ProcessManager integration with StreamManager');
|
|
519
509
|
// Set up tool metrics service to listen to claude messages
|
|
520
510
|
this.toolMetricsService.listenToClaudeMessages(this.processManager);
|
|
511
|
+
// Local map to track streamingId -> sessionId (populated from system_init messages)
|
|
512
|
+
// This is a simple, reliable source of truth that doesn't depend on external services
|
|
513
|
+
const streamingSessionMap = new Map();
|
|
521
514
|
// Forward Claude messages to stream
|
|
522
515
|
this.processManager.on('claude-message', ({ streamingId, message }) => {
|
|
523
516
|
this.logger.debug('Received claude-message event', {
|
|
@@ -528,6 +521,15 @@ export class CUIServer {
|
|
|
528
521
|
contentLength: message?.content?.length || 0,
|
|
529
522
|
messageKeys: message ? Object.keys(message) : []
|
|
530
523
|
});
|
|
524
|
+
// Capture sessionId from system init messages (this is our source of truth)
|
|
525
|
+
if (message && message.type === 'system' && message.subtype === 'init' && message.session_id) {
|
|
526
|
+
streamingSessionMap.set(streamingId, message.session_id);
|
|
527
|
+
this.logger.info('[SESSION-MAP] Captured sessionId from system_init', {
|
|
528
|
+
streamingId: streamingId.slice(0, 8),
|
|
529
|
+
sessionId: message.session_id.slice(0, 8),
|
|
530
|
+
mapSize: streamingSessionMap.size
|
|
531
|
+
});
|
|
532
|
+
}
|
|
531
533
|
// Skip broadcasting system init messages as they're now included in API response
|
|
532
534
|
if (message && message.type === 'system' && message.subtype === 'init') {
|
|
533
535
|
this.logger.debug('Skipping broadcast of system init message (included in API response)', {
|
|
@@ -536,15 +538,83 @@ export class CUIServer {
|
|
|
536
538
|
});
|
|
537
539
|
return;
|
|
538
540
|
}
|
|
539
|
-
// Detect AskUserQuestion tool_use and register with QuestionTracker
|
|
541
|
+
// Detect AskUserQuestion tool_use and register with QuestionTracker + persist to DB
|
|
542
|
+
// Debug: log all assistant messages to see what structure we're getting
|
|
543
|
+
if (message && message.type === 'assistant') {
|
|
544
|
+
this.logger.debug('Assistant message received', {
|
|
545
|
+
streamingId,
|
|
546
|
+
hasMessage: !!message.message,
|
|
547
|
+
hasContent: !!message.message?.content,
|
|
548
|
+
contentIsArray: Array.isArray(message.message?.content),
|
|
549
|
+
contentLength: Array.isArray(message.message?.content) ? message.message.content.length : 0
|
|
550
|
+
});
|
|
551
|
+
}
|
|
540
552
|
if (message && message.type === 'assistant' && message.message?.content) {
|
|
541
553
|
const content = message.message.content;
|
|
542
554
|
if (Array.isArray(content)) {
|
|
543
555
|
for (const block of content) {
|
|
556
|
+
// Debug: log each block to see tool_use blocks
|
|
557
|
+
if (block.type === 'tool_use') {
|
|
558
|
+
this.logger.info('Tool use block detected', {
|
|
559
|
+
streamingId,
|
|
560
|
+
toolName: block.name,
|
|
561
|
+
blockId: block.id
|
|
562
|
+
});
|
|
563
|
+
}
|
|
544
564
|
if (block.type === 'tool_use' && block.name === 'AskUserQuestion') {
|
|
565
|
+
// Debug: log session state when AskUserQuestion arrives
|
|
566
|
+
const activeSessions = this.conversationStatusManager.getActiveSessionIds();
|
|
567
|
+
this.logger.info('AskUserQuestion detected - checking session state', {
|
|
568
|
+
incomingStreamingId: streamingId,
|
|
569
|
+
activeSessions,
|
|
570
|
+
activeCount: activeSessions.length
|
|
571
|
+
});
|
|
545
572
|
const input = block.input;
|
|
546
573
|
if (input.questions) {
|
|
547
|
-
|
|
574
|
+
// Add to in-memory tracker (for active session UI)
|
|
575
|
+
const questionRequest = this.questionTracker.addQuestionRequest(block.id, input.questions, streamingId);
|
|
576
|
+
// Persist to DB (for recovery after timeout/disconnect)
|
|
577
|
+
// Use local streamingSessionMap as primary source (populated from system_init)
|
|
578
|
+
// Fall back to other sources if needed
|
|
579
|
+
let claudeSessionId = streamingSessionMap.get(streamingId);
|
|
580
|
+
if (!claudeSessionId) {
|
|
581
|
+
claudeSessionId = this.conversationStatusManager.getSessionId(streamingId);
|
|
582
|
+
if (claudeSessionId) {
|
|
583
|
+
this.logger.debug('Using conversationStatusManager sessionId fallback', { streamingId, claudeSessionId });
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
if (!claudeSessionId) {
|
|
587
|
+
claudeSessionId = this.processManager.getClaudeSessionId(streamingId);
|
|
588
|
+
if (claudeSessionId) {
|
|
589
|
+
this.logger.debug('Using processManager sessionId fallback', { streamingId, claudeSessionId });
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
if (claudeSessionId) {
|
|
593
|
+
this.pendingQuestionService.addQuestion(questionRequest.id, claudeSessionId, streamingId, block.id, input.questions);
|
|
594
|
+
this.logger.info('AskUserQuestion persisted for recovery', {
|
|
595
|
+
questionId: questionRequest.id,
|
|
596
|
+
sessionId: claudeSessionId,
|
|
597
|
+
streamingId,
|
|
598
|
+
questionCount: input.questions.length
|
|
599
|
+
});
|
|
600
|
+
// Stop the session immediately to prevent Claude from continuing
|
|
601
|
+
// The session will be resumed when the user answers the question
|
|
602
|
+
this.logger.info('Stopping session to await user answer', { streamingId, claudeSessionId });
|
|
603
|
+
this.processManager.stopConversation(streamingId).catch((err) => {
|
|
604
|
+
this.logger.warn('Failed to stop session after AskUserQuestion', {
|
|
605
|
+
streamingId,
|
|
606
|
+
error: err instanceof Error ? err.message : String(err)
|
|
607
|
+
});
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
else {
|
|
611
|
+
this.logger.warn('AskUserQuestion NOT persisted - claudeSessionId not found for streamingId', {
|
|
612
|
+
questionId: questionRequest.id,
|
|
613
|
+
streamingId,
|
|
614
|
+
questionCount: input.questions.length,
|
|
615
|
+
activeSessionCount: this.conversationStatusManager.getActiveSessionIds().length
|
|
616
|
+
});
|
|
617
|
+
}
|
|
548
618
|
}
|
|
549
619
|
}
|
|
550
620
|
}
|
|
@@ -566,9 +636,23 @@ export class CUIServer {
|
|
|
566
636
|
clientCount: this.streamManager.getClientCount(streamingId),
|
|
567
637
|
wasSuccessful: code === 0
|
|
568
638
|
});
|
|
639
|
+
// Check for pending questions BEFORE unregistering (we need the sessionId mapping)
|
|
640
|
+
// If there are pending questions, this was likely an AskUserQuestion timeout
|
|
641
|
+
const pendingQuestions = this.pendingQuestionService.getPendingByStreamingId(streamingId);
|
|
642
|
+
if (pendingQuestions.length > 0) {
|
|
643
|
+
this.logger.info('Session closed with pending AskUserQuestion - questions preserved for later resume', {
|
|
644
|
+
streamingId,
|
|
645
|
+
pendingQuestionCount: pendingQuestions.length,
|
|
646
|
+
questionIds: pendingQuestions.map(q => q.id)
|
|
647
|
+
});
|
|
648
|
+
// Questions remain in DB with status='pending' for later retrieval
|
|
649
|
+
// The in-memory tracker will be cleaned up below, but DB persists
|
|
650
|
+
}
|
|
651
|
+
// Clean up local session map
|
|
652
|
+
streamingSessionMap.delete(streamingId);
|
|
569
653
|
// Unregister session from status tracker
|
|
570
654
|
this.logger.debug('Unregistering session from status tracker', { streamingId });
|
|
571
|
-
this.
|
|
655
|
+
this.conversationStatusManager.unregisterActiveSession(streamingId);
|
|
572
656
|
// Clean up conversation context (handled automatically in unregisterActiveSession)
|
|
573
657
|
// Clean up permissions for this streaming session
|
|
574
658
|
const removedCount = this.permissionTracker.removePermissionsByStreamingId(streamingId);
|
|
@@ -578,14 +662,27 @@ export class CUIServer {
|
|
|
578
662
|
removedPermissions: removedCount
|
|
579
663
|
});
|
|
580
664
|
}
|
|
581
|
-
//
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
665
|
+
// Check for in-memory questions BEFORE removing them
|
|
666
|
+
// If there are pending questions, we need to keep them for error suppression
|
|
667
|
+
// They'll be cleaned up when answered or expired, not on session close
|
|
668
|
+
const inMemoryQuestions = this.questionTracker.getQuestionsByStreamingId(streamingId);
|
|
669
|
+
if (inMemoryQuestions.length > 0) {
|
|
670
|
+
this.logger.info('Preserving in-memory questions for error suppression (awaiting user answer)', {
|
|
585
671
|
streamingId,
|
|
586
|
-
|
|
672
|
+
inMemoryQuestionCount: inMemoryQuestions.length,
|
|
673
|
+
questionIds: inMemoryQuestions.map(q => q.id)
|
|
587
674
|
});
|
|
588
675
|
}
|
|
676
|
+
else {
|
|
677
|
+
// No pending questions - safe to clean up
|
|
678
|
+
const removedQuestions = this.questionTracker.removeQuestionsByStreamingId(streamingId);
|
|
679
|
+
if (removedQuestions > 0) {
|
|
680
|
+
this.logger.debug('Cleaned up in-memory questions for closed session', {
|
|
681
|
+
streamingId,
|
|
682
|
+
removedQuestions
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
}
|
|
589
686
|
if (code === 0) {
|
|
590
687
|
// Session completion notification removed
|
|
591
688
|
}
|
|
@@ -593,15 +690,28 @@ export class CUIServer {
|
|
|
593
690
|
});
|
|
594
691
|
// Handle process errors
|
|
595
692
|
this.processManager.on('process-error', ({ streamingId, error }) => {
|
|
596
|
-
this.logger.debug('Received process-error event
|
|
693
|
+
this.logger.debug('Received process-error event', {
|
|
597
694
|
streamingId,
|
|
598
695
|
error,
|
|
599
696
|
errorLength: error?.toString().length || 0,
|
|
600
697
|
clientCount: this.streamManager.getClientCount(streamingId)
|
|
601
698
|
});
|
|
699
|
+
// Check if there's a pending question for this streaming session
|
|
700
|
+
// If so, suppress the error - the session was intentionally stopped to wait for user answer
|
|
701
|
+
const pendingQuestions = this.questionTracker.getQuestionsByStreamingId(streamingId);
|
|
702
|
+
if (pendingQuestions.length > 0) {
|
|
703
|
+
this.logger.info('Suppressing process error due to pending AskUserQuestion', {
|
|
704
|
+
streamingId,
|
|
705
|
+
pendingQuestionCount: pendingQuestions.length,
|
|
706
|
+
questionIds: pendingQuestions.map(q => q.id)
|
|
707
|
+
});
|
|
708
|
+
// Still unregister the session but don't broadcast the error
|
|
709
|
+
this.conversationStatusManager.unregisterActiveSession(streamingId);
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
602
712
|
// Unregister session from status tracker on error
|
|
603
713
|
this.logger.debug('Unregistering session from status tracker due to error', { streamingId });
|
|
604
|
-
this.
|
|
714
|
+
this.conversationStatusManager.unregisterActiveSession(streamingId);
|
|
605
715
|
// Clean up conversation context on error (handled automatically in unregisterActiveSession)
|
|
606
716
|
const errorEvent = {
|
|
607
717
|
type: 'error',
|
|
@@ -648,11 +758,13 @@ export class CUIServer {
|
|
|
648
758
|
this.logger.debug('Setting up QuestionTracker integration');
|
|
649
759
|
// Forward question events to stream
|
|
650
760
|
this.questionTracker.on('question_request', (request) => {
|
|
651
|
-
|
|
761
|
+
// Log at info level to diagnose question flow issues
|
|
762
|
+
this.logger.info('Question request event received, broadcasting to stream', {
|
|
652
763
|
id: request.id,
|
|
653
764
|
toolUseId: request.toolUseId,
|
|
654
765
|
streamingId: request.streamingId,
|
|
655
|
-
questionCount: request.questions.length
|
|
766
|
+
questionCount: request.questions.length,
|
|
767
|
+
clientCount: this.streamManager.getClientCount(request.streamingId)
|
|
656
768
|
});
|
|
657
769
|
// Broadcast to the appropriate streaming session
|
|
658
770
|
if (request.streamingId && request.streamingId !== 'unknown') {
|