codehere 0.2.2 → 0.3.0-rc.1
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 +88 -68
- package/dist/domain/entities/task.js +4 -4
- package/dist/error-handler.js +4 -9
- package/dist/index.js +253 -1219
- package/dist/infrastructure/config/product-context.js +7 -6
- package/dist/infrastructure/hooks/claude-sdk-hook.js +335 -63
- package/dist/infrastructure/observability/execution-tracer.js +11 -6
- package/dist/infrastructure/orchestration/agent-adapter.js +13 -0
- package/dist/infrastructure/orchestration/agent-installer.js +101 -0
- package/dist/infrastructure/orchestration/agents-config-seed.js +56 -0
- package/dist/infrastructure/orchestration/aider-adapter.js +319 -0
- package/dist/infrastructure/orchestration/artifacts-extractor.js +175 -0
- package/dist/infrastructure/orchestration/claude-code-adapter.js +352 -0
- package/dist/infrastructure/orchestration/codex-adapter.js +310 -0
- package/dist/infrastructure/orchestration/generic-cli-adapter.js +553 -0
- package/dist/infrastructure/orchestration/opencode-adapter.js +275 -0
- package/dist/infrastructure/orchestration/progress-aggregator.js +189 -0
- package/dist/infrastructure/orchestration/readiness.js +106 -0
- package/dist/infrastructure/orchestration/session-manager.js +272 -0
- package/dist/infrastructure/orchestration/session-persistence.js +109 -0
- package/dist/infrastructure/protocols/mcp-helpers.js +10 -30
- package/dist/infrastructure/protocols/mcp-server-impl.js +359 -21
- package/dist/infrastructure/protocols/ui-page.js +1827 -0
- package/dist/infrastructure/security/file-collection.js +115 -0
- package/dist/infrastructure/security/trust-scorer.js +301 -56
- package/dist/infrastructure/storage/task-repository.js +8 -7
- package/dist/monitoring.js +9 -38
- package/dist/presentation/cli/commands/help-command.js +96 -139
- package/dist/presentation/cli/commands/hook-command.js +17 -24
- package/dist/presentation/cli/commands/report-command.js +59 -72
- package/dist/presentation/cli/commands/setup-command.js +4 -4
- package/dist/presentation/cli/commands/status-command.js +321 -30
- package/dist/presentation/cli/commands/trust-command.js +84 -72
- package/dist/presentation/cli/error-display.js +17 -21
- package/dist/ui.js +11 -11
- package/package.json +24 -62
- package/dist/agents/base.js +0 -101
- package/dist/agents/orchestrator-model-driven.js +0 -141
- package/dist/agents/react-orchestrator-agent.js +0 -54
- package/dist/application/agents/execution-agent.js +0 -508
- package/dist/application/agents/multi-agent-orchestrator.js +0 -295
- package/dist/application/agents/planning-agent.js +0 -433
- package/dist/application/agents/validation-agent.js +0 -317
- package/dist/application/architectures/plan-and-act.js +0 -242
- package/dist/application/index.js +0 -12
- package/dist/application/services/agent-factory.js +0 -28
- package/dist/application/services/dependency-container.js +0 -596
- package/dist/application/services/git-context-service.js +0 -83
- package/dist/application/services/intelligent-router.js +0 -934
- package/dist/application/services/tool-executor-service.js +0 -94
- package/dist/application/use-cases/ask-question-use-case.js +0 -338
- package/dist/application/use-cases/edit-file-use-case.js +0 -830
- package/dist/application/use-cases/explain-file-use-case.js +0 -50
- package/dist/application/use-cases/index-codebase-use-case.js +0 -113
- package/dist/application/use-cases/planning-use-case.js +0 -276
- package/dist/application/use-cases/react-orchestration-use-case.js +0 -329
- package/dist/application/use-cases/reflexion-use-case.js +0 -30
- package/dist/application/workflows/deterministic-workflows.js +0 -189
- package/dist/ast-parser.js +0 -331
- package/dist/cache.js +0 -180
- package/dist/chat.js +0 -251
- package/dist/code-analysis.js +0 -327
- package/dist/context.js +0 -187
- package/dist/docs.js +0 -146
- package/dist/domain/entities/code-chunk.js +0 -6
- package/dist/domain/entities/decision.js +0 -9
- package/dist/domain/entities/edit.js +0 -6
- package/dist/domain/entities/knowledge-node.js +0 -9
- package/dist/domain/entities/knowledge-preservation.js +0 -10
- package/dist/domain/entities/memory.js +0 -6
- package/dist/domain/entities/pattern.js +0 -7
- package/dist/domain/entities/plan.js +0 -6
- package/dist/domain/entities/query.js +0 -6
- package/dist/domain/errors/embeddings-not-found-error.js +0 -13
- package/dist/domain/index.js +0 -19
- package/dist/domain/interfaces/ai-service.interface.js +0 -6
- package/dist/domain/interfaces/code-analyzer.interface.js +0 -6
- package/dist/domain/interfaces/decision-repository.interface.js +0 -9
- package/dist/domain/interfaces/embedding-repository.interface.js +0 -6
- package/dist/domain/interfaces/knowledge-graph-repository.interface.js +0 -9
- package/dist/domain/interfaces/knowledge-preservation-repository.interface.js +0 -8
- package/dist/domain/interfaces/memory-repository.interface.js +0 -6
- package/dist/domain/interfaces/pattern-repository.interface.js +0 -6
- package/dist/domain/interfaces/verification-service.interface.js +0 -6
- package/dist/domain/services/cross-team-pattern-sharing.js +0 -125
- package/dist/domain/services/decision-recorder.js +0 -223
- package/dist/domain/services/decision-retriever.js +0 -133
- package/dist/domain/services/environment-mode-manager.js +0 -22
- package/dist/domain/services/graded-memory-activation.js +0 -69
- package/dist/domain/services/knowledge-access.js +0 -132
- package/dist/domain/services/knowledge-extractor.js +0 -247
- package/dist/domain/services/knowledge-graph-builder.js +0 -268
- package/dist/domain/services/knowledge-graph-query.js +0 -187
- package/dist/domain/services/memory-retriever.js +0 -82
- package/dist/domain/services/memory-synthesizer.js +0 -91
- package/dist/domain/services/pattern-extractor.js +0 -442
- package/dist/domain/services/query-intent-classifier.js +0 -140
- package/dist/domain/services/react-loop.js +0 -186
- package/dist/domain/services/risk-tier-detector.js +0 -78
- package/dist/edit.js +0 -319
- package/dist/embed.js +0 -459
- package/dist/formatter.js +0 -168
- package/dist/git.js +0 -198
- package/dist/infrastructure/ai/cohere-ai-service.js +0 -607
- package/dist/infrastructure/ai/command-r-evaluation.js +0 -206
- package/dist/infrastructure/ai/model-config.js +0 -84
- package/dist/infrastructure/ai/provider-ai-service.js +0 -477
- package/dist/infrastructure/cache/embedding-cache.js +0 -175
- package/dist/infrastructure/cache/query-result-cache.js +0 -66
- package/dist/infrastructure/cache/response-cache.js +0 -48
- package/dist/infrastructure/cache/security-scan-cache.js +0 -47
- package/dist/infrastructure/chunking/ast-code-chunker.js +0 -19
- package/dist/infrastructure/chunking/code-aware-chunker.js +0 -397
- package/dist/infrastructure/context/context-compressor.js +0 -329
- package/dist/infrastructure/context/conversation-context.js +0 -128
- package/dist/infrastructure/context/session-manager.js +0 -94
- package/dist/infrastructure/cost/cost-tracker.js +0 -360
- package/dist/infrastructure/file-scanner/file-scanner.js +0 -106
- package/dist/infrastructure/governance/nist-ai-rmf.js +0 -292
- package/dist/infrastructure/governance/policy-as-code.js +0 -258
- package/dist/infrastructure/governance/prompt-to-code-lineage.js +0 -239
- package/dist/infrastructure/index.js +0 -7
- package/dist/infrastructure/observability/prompt-versioner.js +0 -145
- package/dist/infrastructure/protocols/a2a-metadata.js +0 -137
- package/dist/infrastructure/protocols/a2a-protocol.js +0 -314
- package/dist/infrastructure/protocols/mcp-oauth.js +0 -184
- package/dist/infrastructure/protocols/mcp-server.js +0 -333
- package/dist/infrastructure/protocols/streaming-handler.js +0 -103
- package/dist/infrastructure/providers/cohere-provider.js +0 -444
- package/dist/infrastructure/providers/fake-provider.js +0 -130
- package/dist/infrastructure/providers/local-llm-provider.js +0 -330
- package/dist/infrastructure/providers/model-config.types.js +0 -9
- package/dist/infrastructure/providers/model-provider.interface.js +0 -8
- package/dist/infrastructure/providers/openai-provider.js +0 -413
- package/dist/infrastructure/providers/openrouter-provider.js +0 -399
- package/dist/infrastructure/providers/operation-provider-resolver.js +0 -105
- package/dist/infrastructure/providers/provider-config-loader.js +0 -120
- package/dist/infrastructure/providers/provider-registry.js +0 -88
- package/dist/infrastructure/safety/pattern-detector.js +0 -133
- package/dist/infrastructure/security/ai-sast-scanner.js +0 -290
- package/dist/infrastructure/security/docker-sandbox.js +0 -178
- package/dist/infrastructure/security/enhanced-security-gate.js +0 -151
- package/dist/infrastructure/security/license-scanner.js +0 -167
- package/dist/infrastructure/security/provider-bias-detector.js +0 -164
- package/dist/infrastructure/security/sandbox-executor.js +0 -64
- package/dist/infrastructure/serialization/toon-serializer.js +0 -119
- package/dist/infrastructure/storage/memory-stream-service.js +0 -204
- package/dist/infrastructure/storage/plan-repository.js +0 -96
- package/dist/infrastructure/storage/sqlite-decision-repository.js +0 -232
- package/dist/infrastructure/storage/sqlite-embedding-repository.js +0 -311
- package/dist/infrastructure/storage/sqlite-knowledge-graph-repository.js +0 -417
- package/dist/infrastructure/storage/sqlite-knowledge-preservation-repository.js +0 -230
- package/dist/infrastructure/storage/sqlite-memory-repository.js +0 -229
- package/dist/infrastructure/storage/sqlite-pattern-repository.js +0 -249
- package/dist/infrastructure/storage/task-helpers.js +0 -197
- package/dist/infrastructure/telemetry/telemetry-service.js +0 -167
- package/dist/infrastructure/ux/appropriate-friction.js +0 -213
- package/dist/infrastructure/ux/bi-directional-learning.js +0 -337
- package/dist/infrastructure/ux/capability-boundaries.js +0 -42
- package/dist/infrastructure/ux/comprehensive-formatter.js +0 -117
- package/dist/infrastructure/ux/contextual-feature-discovery.js +0 -10
- package/dist/infrastructure/ux/expectation-management.js +0 -327
- package/dist/infrastructure/ux/feature-discovery.js +0 -14
- package/dist/infrastructure/ux/feedback-prompt.js +0 -45
- package/dist/infrastructure/ux/hitl-validation.js +0 -297
- package/dist/infrastructure/ux/progress-indicator.js +0 -116
- package/dist/infrastructure/ux/progressive-disclosure.js +0 -236
- package/dist/infrastructure/ux/review-checkpoint.js +0 -72
- package/dist/infrastructure/ux/staged-feature-intro.js +0 -24
- package/dist/infrastructure/ux/stream-markdown.js +0 -151
- package/dist/infrastructure/ux/syntax-highlighter.js +0 -91
- package/dist/infrastructure/ux/team-standards.js +0 -196
- package/dist/infrastructure/ux/transparency.js +0 -71
- package/dist/infrastructure/validation/agent-feedback.js +0 -55
- package/dist/infrastructure/validation/agent-validation-helper.js +0 -137
- package/dist/infrastructure/validation/review-handler.js +0 -40
- package/dist/infrastructure/validation/summary-aggregator.js +0 -70
- package/dist/infrastructure/validation/summary-extractor.js +0 -45
- package/dist/infrastructure/validation/trace-summary.js +0 -52
- package/dist/infrastructure/verification/verification-service.js +0 -47
- package/dist/infrastructure/xai/attention-visualizer.js +0 -172
- package/dist/infrastructure/xai/cot-visualizer.js +0 -283
- package/dist/infrastructure/xai/decision-tree-log.js +0 -212
- package/dist/infrastructure/xai/interactive-xai.js +0 -260
- package/dist/infrastructure/xai/uncertainty-quantifier.js +0 -198
- package/dist/intelligent-retrieval.js +0 -272
- package/dist/intent/classifier.js +0 -2
- package/dist/intent/context.js +0 -2
- package/dist/iterative-refinement.js +0 -189
- package/dist/learning.js +0 -232
- package/dist/log.js +0 -31
- package/dist/migrate.js +0 -133
- package/dist/parallel-processor.js +0 -534
- package/dist/planner.js +0 -198
- package/dist/policy.js +0 -26
- package/dist/presentation/cli/commands/ask-command.js +0 -314
- package/dist/presentation/cli/commands/cost-command.js +0 -191
- package/dist/presentation/cli/commands/decision-command.js +0 -207
- package/dist/presentation/cli/commands/docs-command.js +0 -25
- package/dist/presentation/cli/commands/fix-command.js +0 -53
- package/dist/presentation/cli/commands/knowledge-command.js +0 -129
- package/dist/presentation/cli/commands/learning-dashboard-command.js +0 -183
- package/dist/presentation/cli/commands/models-command.js +0 -194
- package/dist/presentation/cli/commands/orchestrate-command.js +0 -207
- package/dist/presentation/cli/commands/plan-command.js +0 -246
- package/dist/presentation/cli/commands/product-command.js +0 -199
- package/dist/presentation/cli/commands/react-command.js +0 -181
- package/dist/presentation/cli/commands/review-command.js +0 -154
- package/dist/presentation/cli/commands/run-command.js +0 -188
- package/dist/presentation/cli/commands/smart-command.js +0 -395
- package/dist/presentation/cli/commands/suggest-command.js +0 -138
- package/dist/presentation/cli/commands/trace-command.js +0 -129
- package/dist/presentation/cli/commands/undo-command.js +0 -150
- package/dist/query-optimizer.js +0 -205
- package/dist/refactor.js +0 -116
- package/dist/review.js +0 -206
- package/dist/search.js +0 -163
- package/dist/session.js +0 -704
- package/dist/task-verification.js +0 -336
- package/dist/testgen.js +0 -166
- package/dist/token-optimizer.js +0 -277
- package/dist/tools/bash.js +0 -213
- package/dist/tools/edit.js +0 -235
- package/dist/tools/executor.js +0 -153
- package/dist/tools/git-tool.js +0 -292
- package/dist/tools/parser.js +0 -158
- package/dist/tools/registry.js +0 -302
- package/dist/tools/search.js +0 -100
- package/dist/types.js +0 -2
- package/dist/utils/check-api-status.js +0 -100
- package/dist/utils/diff-parser.js +0 -162
- package/dist/verification/backup.js +0 -259
- package/dist/verification/compiler.js +0 -264
- package/dist/verification/index.js +0 -273
- package/dist/verification/test-runner.js +0 -379
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory registry of active task sessions + audit-log derived observation.
|
|
3
|
+
*
|
|
4
|
+
* Two kinds of session surfaced to the UI:
|
|
5
|
+
* 1. spawned — started via codehere.spawn; we own the subprocess, can kill.
|
|
6
|
+
* 2. observed — derived from the audit log. Claude Code sessions running
|
|
7
|
+
* in the user's terminals, IDE, or elsewhere on this machine.
|
|
8
|
+
* codehere didn't start them and can't kill them, but shows
|
|
9
|
+
* them so the dashboard reflects reality from first load.
|
|
10
|
+
*
|
|
11
|
+
* Durable state lives in the audit log (F0). On daemon restart, spawned
|
|
12
|
+
* sessions are gone but observed ones remain (they're re-derived from the log).
|
|
13
|
+
*/
|
|
14
|
+
import { existsSync, readFileSync } from 'fs';
|
|
15
|
+
import { execFileSync } from 'child_process';
|
|
16
|
+
import { ClaudeCodeAdapter } from './claude-code-adapter.js';
|
|
17
|
+
import { CodexAdapter } from './codex-adapter.js';
|
|
18
|
+
import { AiderAdapter } from './aider-adapter.js';
|
|
19
|
+
import { OpenCodeAdapter } from './opencode-adapter.js';
|
|
20
|
+
import { buildGenericAdapter, loadAgentsConfig } from './generic-cli-adapter.js';
|
|
21
|
+
import { seedAgentsConfig } from './agents-config-seed.js';
|
|
22
|
+
import { AUDIT_FILE } from '../hooks/claude-sdk-hook.js';
|
|
23
|
+
import { appendSpawned, appendCompleted, loadRecent } from './session-persistence.js';
|
|
24
|
+
// ── Agent registry ─────────────────────────────────────────────────────────────
|
|
25
|
+
// All backends codehere can spawn. Order here drives the default UI dropdown
|
|
26
|
+
// order. Built-in adapters first; user-config-driven ones appended from
|
|
27
|
+
// ~/.codehere/agents.yaml (generic-cli-adapter). isInstalled() gates visibility
|
|
28
|
+
// so an adapter registering for an un-installed CLI is harmless.
|
|
29
|
+
const BUILTIN_REGISTRY = [ClaudeCodeAdapter, CodexAdapter, AiderAdapter, OpenCodeAdapter];
|
|
30
|
+
// Seed the default agents.yaml if this user has never configured one, so Gemini
|
|
31
|
+
// / Cursor / Pi / Antigravity / Goose appear in the registry pre-wired. They
|
|
32
|
+
// stay hidden in the UI until isInstalled() flips to true. Idempotent.
|
|
33
|
+
seedAgentsConfig();
|
|
34
|
+
const REGISTRY = [
|
|
35
|
+
...BUILTIN_REGISTRY,
|
|
36
|
+
// User-configurable adapters — "plug any CLI" escape hatch, seeded with
|
|
37
|
+
// popular CLI agents above. Users can add/edit/remove entries freely.
|
|
38
|
+
...loadAgentsConfig().map(buildGenericAdapter),
|
|
39
|
+
];
|
|
40
|
+
export function availableAgents() {
|
|
41
|
+
return REGISTRY.map((a) => ({ id: a.id, label: a.label, installed: a.isInstalled() }));
|
|
42
|
+
}
|
|
43
|
+
/** Per-agent readiness across the full registry. Powers the setup-aware UI. */
|
|
44
|
+
export function readinessForAllAgents() {
|
|
45
|
+
return REGISTRY.map((a) => {
|
|
46
|
+
if (a.checkReadiness)
|
|
47
|
+
return a.checkReadiness();
|
|
48
|
+
// Fallback for adapters that haven't implemented checkReadiness yet: treat
|
|
49
|
+
// "installed" as "ready", "not installed" as "needs_install without hint".
|
|
50
|
+
const installed = a.isInstalled();
|
|
51
|
+
return installed
|
|
52
|
+
? { id: a.id, label: a.label, state: 'ready', installed: true }
|
|
53
|
+
: { id: a.id, label: a.label, state: 'needs_install', installed: false };
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
export function getAdapter(id) {
|
|
57
|
+
return REGISTRY.find((a) => a.id === id);
|
|
58
|
+
}
|
|
59
|
+
export function allAdapters() {
|
|
60
|
+
return REGISTRY;
|
|
61
|
+
}
|
|
62
|
+
export function agentProcessPatterns() {
|
|
63
|
+
return REGISTRY.map((a) => ({
|
|
64
|
+
regex: a.processPattern,
|
|
65
|
+
agent: a.id,
|
|
66
|
+
label: a.label,
|
|
67
|
+
}));
|
|
68
|
+
}
|
|
69
|
+
const DEFAULT_OBSERVED_WINDOW_MS = 10 * 60 * 1000; // 10 minutes
|
|
70
|
+
function parseEtime(s) {
|
|
71
|
+
// ps etime formats: "MM:SS", "HH:MM:SS", "D-HH:MM:SS"
|
|
72
|
+
let rest = s;
|
|
73
|
+
let days = 0;
|
|
74
|
+
if (rest.includes('-')) {
|
|
75
|
+
const [d, r] = rest.split('-');
|
|
76
|
+
days = parseInt(d, 10) || 0;
|
|
77
|
+
rest = r;
|
|
78
|
+
}
|
|
79
|
+
const parts = rest.split(':').map((p) => parseInt(p, 10) || 0);
|
|
80
|
+
if (parts.length === 2)
|
|
81
|
+
return parts[0] * 60 + parts[1];
|
|
82
|
+
if (parts.length === 3)
|
|
83
|
+
return days * 86400 + parts[0] * 3600 + parts[1] * 60 + parts[2];
|
|
84
|
+
return 0;
|
|
85
|
+
}
|
|
86
|
+
export class SessionManager {
|
|
87
|
+
sessions = new Map();
|
|
88
|
+
/** Read-only sessions replayed from ~/.codehere/sessions.jsonl on daemon start.
|
|
89
|
+
* These subprocesses are long-dead; we surface them so the user doesn't lose
|
|
90
|
+
* their Final reports across restarts. */
|
|
91
|
+
persistedCache = loadRecent(20);
|
|
92
|
+
spawn(prompt, agentId = 'claude-code', options) {
|
|
93
|
+
const adapter = getAdapter(agentId);
|
|
94
|
+
if (!adapter) {
|
|
95
|
+
throw new Error(`Unknown agent: ${agentId}. Registered: ${REGISTRY.map((a) => a.id).join(', ')}`);
|
|
96
|
+
}
|
|
97
|
+
if (!adapter.isInstalled()) {
|
|
98
|
+
throw new Error(`Agent ${adapter.label} (${adapter.id}) is not installed on this machine`);
|
|
99
|
+
}
|
|
100
|
+
const session = adapter.spawn(prompt, options);
|
|
101
|
+
this.sessions.set(session.sessionId, session);
|
|
102
|
+
// Persistence: write the spawned record immediately so a daemon crash still
|
|
103
|
+
// leaves an audit trail of what was attempted. Subscribe to exit so we can
|
|
104
|
+
// capture the final status + steps + artifacts when the session settles.
|
|
105
|
+
const cwd = options?.cwd ?? process.cwd();
|
|
106
|
+
appendSpawned({
|
|
107
|
+
id: session.sessionId,
|
|
108
|
+
agent: session.agent,
|
|
109
|
+
prompt,
|
|
110
|
+
cwd,
|
|
111
|
+
startedAt: session.startedAt,
|
|
112
|
+
});
|
|
113
|
+
const unsubscribe = session.subscribe((event) => {
|
|
114
|
+
if (event.type !== 'exit')
|
|
115
|
+
return;
|
|
116
|
+
const data = event.data;
|
|
117
|
+
appendCompleted({
|
|
118
|
+
id: session.sessionId,
|
|
119
|
+
status: session.status,
|
|
120
|
+
durationMs: Number(data?.durationMs ?? Date.now() - session.startedAt),
|
|
121
|
+
completedAt: Date.now(),
|
|
122
|
+
steps: session.steps ? [...session.steps] : undefined,
|
|
123
|
+
artifacts: session.artifacts,
|
|
124
|
+
});
|
|
125
|
+
unsubscribe();
|
|
126
|
+
});
|
|
127
|
+
return session;
|
|
128
|
+
}
|
|
129
|
+
/** Persisted (read-only) sessions from prior daemon runs. Cached at boot. */
|
|
130
|
+
listPersisted() {
|
|
131
|
+
return this.persistedCache;
|
|
132
|
+
}
|
|
133
|
+
get(sessionId) {
|
|
134
|
+
return this.sessions.get(sessionId);
|
|
135
|
+
}
|
|
136
|
+
list() {
|
|
137
|
+
return Array.from(this.sessions.values());
|
|
138
|
+
}
|
|
139
|
+
kill(sessionId) {
|
|
140
|
+
const session = this.sessions.get(sessionId);
|
|
141
|
+
if (!session)
|
|
142
|
+
return false;
|
|
143
|
+
session.kill();
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Active Claude Code sessions on this machine, derived from the audit log.
|
|
148
|
+
* A session is "active" if any tool call from it was recorded within the
|
|
149
|
+
* last `maxAgeMs` milliseconds. Returns most-recent first.
|
|
150
|
+
*
|
|
151
|
+
* Note: codehere-spawned sessions may also appear here because they produce
|
|
152
|
+
* audit entries just like any other Claude Code session. The UI distinguishes
|
|
153
|
+
* by provenance.
|
|
154
|
+
*/
|
|
155
|
+
listObserved(maxAgeMs = DEFAULT_OBSERVED_WINDOW_MS) {
|
|
156
|
+
if (!existsSync(AUDIT_FILE))
|
|
157
|
+
return [];
|
|
158
|
+
const now = Date.now();
|
|
159
|
+
const cutoff = now - maxAgeMs;
|
|
160
|
+
const byId = new Map();
|
|
161
|
+
let content;
|
|
162
|
+
try {
|
|
163
|
+
content = readFileSync(AUDIT_FILE, 'utf-8');
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
return [];
|
|
167
|
+
}
|
|
168
|
+
const lines = content.split('\n');
|
|
169
|
+
// Walk backwards — most recent lines first — and stop early when we
|
|
170
|
+
// pass the window. Audit log appends chronologically.
|
|
171
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
172
|
+
const line = lines[i].trim();
|
|
173
|
+
if (!line)
|
|
174
|
+
continue;
|
|
175
|
+
let entry;
|
|
176
|
+
try {
|
|
177
|
+
entry = JSON.parse(line);
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
if (!entry.sessionId)
|
|
183
|
+
continue;
|
|
184
|
+
const ts = typeof entry.timestamp === 'number'
|
|
185
|
+
? entry.timestamp
|
|
186
|
+
: entry.timestamp
|
|
187
|
+
? Date.parse(String(entry.timestamp))
|
|
188
|
+
: 0;
|
|
189
|
+
if (!ts)
|
|
190
|
+
continue;
|
|
191
|
+
if (ts < cutoff)
|
|
192
|
+
break; // past the window; older entries can't qualify
|
|
193
|
+
const existing = byId.get(entry.sessionId);
|
|
194
|
+
if (existing) {
|
|
195
|
+
existing.eventCount += 1;
|
|
196
|
+
if (ts < existing.firstEventAt)
|
|
197
|
+
existing.firstEventAt = ts;
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
byId.set(entry.sessionId, {
|
|
201
|
+
sessionId: entry.sessionId,
|
|
202
|
+
firstEventAt: ts,
|
|
203
|
+
lastEventAt: ts,
|
|
204
|
+
eventCount: 1,
|
|
205
|
+
recentTool: entry.toolName,
|
|
206
|
+
provenance: 'observed',
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return Array.from(byId.values()).sort((a, b) => b.lastEventAt - a.lastEventAt);
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Agent processes running on this machine right now.
|
|
214
|
+
* Enumerates via `ps`, filters to known agent executables, returns most
|
|
215
|
+
* recently started first. This is the primary F1d surface — it reflects
|
|
216
|
+
* "what AI agents are alive on your machine" independent of whether
|
|
217
|
+
* they've produced audit events yet.
|
|
218
|
+
*/
|
|
219
|
+
listActiveAgents() {
|
|
220
|
+
let out;
|
|
221
|
+
try {
|
|
222
|
+
out = execFileSync('ps', ['-A', '-o', 'pid,tty,etime,args'], {
|
|
223
|
+
encoding: 'utf-8',
|
|
224
|
+
timeout: 2000,
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
return [];
|
|
229
|
+
}
|
|
230
|
+
const agents = [];
|
|
231
|
+
const lines = out.trim().split('\n').slice(1); // drop header
|
|
232
|
+
for (const line of lines) {
|
|
233
|
+
const m = line.trim().match(/^(\d+)\s+(\S+)\s+(\S+)\s+(.+)$/);
|
|
234
|
+
if (!m)
|
|
235
|
+
continue;
|
|
236
|
+
const [, pidStr, tty, etime, args] = m;
|
|
237
|
+
// Skip ourselves — the codehere daemon would otherwise show up repeatedly
|
|
238
|
+
if (/\bcodehere\b|\bch\s+start\b/.test(args))
|
|
239
|
+
continue;
|
|
240
|
+
// Skip the audit tailer from other codehere commands
|
|
241
|
+
if (/scan-tool|codehere-audit/.test(args))
|
|
242
|
+
continue;
|
|
243
|
+
let matched = null;
|
|
244
|
+
for (const p of agentProcessPatterns()) {
|
|
245
|
+
if (p.regex.test(args)) {
|
|
246
|
+
matched = { agent: p.agent, label: p.label };
|
|
247
|
+
break;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (!matched)
|
|
251
|
+
continue;
|
|
252
|
+
const uptimeSeconds = parseEtime(etime);
|
|
253
|
+
const startedAt = Date.now() - uptimeSeconds * 1000;
|
|
254
|
+
agents.push({
|
|
255
|
+
pid: parseInt(pidStr, 10),
|
|
256
|
+
tty,
|
|
257
|
+
agent: matched.agent,
|
|
258
|
+
label: matched.label,
|
|
259
|
+
args: args.length > 200 ? args.slice(0, 200) + '…' : args,
|
|
260
|
+
startedAt,
|
|
261
|
+
uptimeSeconds,
|
|
262
|
+
provenance: 'detected',
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
return agents.sort((a, b) => b.startedAt - a.startedAt);
|
|
266
|
+
}
|
|
267
|
+
static _instance;
|
|
268
|
+
static getInstance() {
|
|
269
|
+
return (this._instance ??= new SessionManager());
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
//# sourceMappingURL=session-manager.js.map
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session persistence — survives daemon restart.
|
|
3
|
+
*
|
|
4
|
+
* Beta.3 dogfood surfaced a reliability gap: after `ch start` exited and was
|
|
5
|
+
* relaunched, the task UI was empty even when completed sessions had produced
|
|
6
|
+
* valuable final reports. The audit log (hook-events.jsonl) persisted every
|
|
7
|
+
* tool call but not the session-level PM state (prompt, artifacts, steps).
|
|
8
|
+
*
|
|
9
|
+
* This module adds a minimal two-record schema:
|
|
10
|
+
* { t: 'spawned', id, agent, prompt, cwd, startedAt }
|
|
11
|
+
* { t: 'completed', id, status, durationMs, steps, artifacts, completedAt }
|
|
12
|
+
*
|
|
13
|
+
* On spawn, we write the first line. On exit, we write the second. On daemon
|
|
14
|
+
* start, we read the last N pairs and surface them as read-only "persisted"
|
|
15
|
+
* sessions (no kill button, no live stream; the subprocess died with the
|
|
16
|
+
* previous daemon).
|
|
17
|
+
*
|
|
18
|
+
* Write path stays append-only — never rewrites history. Lock-free because
|
|
19
|
+
* single writer (the daemon). If two daemons ran concurrently the worst case
|
|
20
|
+
* is interleaved lines, which the reader tolerates.
|
|
21
|
+
*/
|
|
22
|
+
import { existsSync, mkdirSync, appendFileSync, readFileSync } from 'fs';
|
|
23
|
+
import { homedir } from 'os';
|
|
24
|
+
import { join, dirname } from 'path';
|
|
25
|
+
const SESSIONS_FILE = join(homedir(), '.codehere', 'sessions.jsonl');
|
|
26
|
+
function ensureDir() {
|
|
27
|
+
const dir = dirname(SESSIONS_FILE);
|
|
28
|
+
if (!existsSync(dir))
|
|
29
|
+
mkdirSync(dir, { recursive: true });
|
|
30
|
+
}
|
|
31
|
+
export function appendSpawned(rec) {
|
|
32
|
+
try {
|
|
33
|
+
ensureDir();
|
|
34
|
+
appendFileSync(SESSIONS_FILE, JSON.stringify({ t: 'spawned', ...rec }) + '\n', 'utf-8');
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
/* persistence is best-effort; never break the spawn path */
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export function appendCompleted(rec) {
|
|
41
|
+
try {
|
|
42
|
+
ensureDir();
|
|
43
|
+
appendFileSync(SESSIONS_FILE, JSON.stringify({ t: 'completed', ...rec }) + '\n', 'utf-8');
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
/* best-effort */
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Walk the session log backward, pair spawned+completed records by id, return
|
|
51
|
+
* the most recent `limit` completed pairs sorted newest-first.
|
|
52
|
+
*
|
|
53
|
+
* We only surface fully-completed sessions because an orphaned `spawned` with
|
|
54
|
+
* no `completed` means either: (a) the daemon died mid-task (subprocess also
|
|
55
|
+
* died, no artifacts), or (b) a concurrent daemon is mid-flight (not ours to
|
|
56
|
+
* show). Either way, not useful in the UI.
|
|
57
|
+
*/
|
|
58
|
+
export function loadRecent(limit = 20) {
|
|
59
|
+
if (!existsSync(SESSIONS_FILE))
|
|
60
|
+
return [];
|
|
61
|
+
let content;
|
|
62
|
+
try {
|
|
63
|
+
content = readFileSync(SESSIONS_FILE, 'utf-8');
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return [];
|
|
67
|
+
}
|
|
68
|
+
const spawned = new Map();
|
|
69
|
+
const completed = [];
|
|
70
|
+
for (const line of content.split('\n')) {
|
|
71
|
+
const trimmed = line.trim();
|
|
72
|
+
if (!trimmed)
|
|
73
|
+
continue;
|
|
74
|
+
let rec;
|
|
75
|
+
try {
|
|
76
|
+
rec = JSON.parse(trimmed);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (rec.t === 'spawned')
|
|
82
|
+
spawned.set(rec.id, rec);
|
|
83
|
+
else if (rec.t === 'completed')
|
|
84
|
+
completed.push(rec);
|
|
85
|
+
}
|
|
86
|
+
const merged = [];
|
|
87
|
+
// Walk completed in reverse so we return newest-first
|
|
88
|
+
for (let i = completed.length - 1; i >= 0 && merged.length < limit; i--) {
|
|
89
|
+
const c = completed[i];
|
|
90
|
+
const s = spawned.get(c.id);
|
|
91
|
+
if (!s)
|
|
92
|
+
continue; // orphan completed (shouldn't happen but harmless)
|
|
93
|
+
merged.push({
|
|
94
|
+
sessionId: s.id,
|
|
95
|
+
agent: s.agent,
|
|
96
|
+
prompt: s.prompt,
|
|
97
|
+
cwd: s.cwd,
|
|
98
|
+
startedAt: s.startedAt,
|
|
99
|
+
status: c.status,
|
|
100
|
+
durationMs: c.durationMs,
|
|
101
|
+
completedAt: c.completedAt,
|
|
102
|
+
steps: c.steps,
|
|
103
|
+
artifacts: c.artifacts,
|
|
104
|
+
provenance: 'persisted',
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
return merged;
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=session-persistence.js.map
|
|
@@ -2,41 +2,21 @@
|
|
|
2
2
|
* MCP helper functions — shared between MCP server and trust command.
|
|
3
3
|
* Extracted to avoid circular imports.
|
|
4
4
|
*/
|
|
5
|
-
import { readFileSync
|
|
6
|
-
import {
|
|
5
|
+
import { readFileSync } from 'fs';
|
|
6
|
+
import { collectCodeFiles } from '../security/file-collection.js';
|
|
7
7
|
// ── File collection ──────────────────────────────────────────────────────────
|
|
8
|
-
|
|
9
|
-
'.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs',
|
|
10
|
-
'.py', '.rb', '.go', '.java', '.cs', '.php',
|
|
11
|
-
'.sh', '.bash', '.zsh',
|
|
12
|
-
'.sql', '.html', '.vue', '.svelte',
|
|
13
|
-
]);
|
|
14
|
-
const IGNORE_DIRS = new Set(['node_modules', '.git', 'dist', 'build', '.next', 'coverage', '.cache', '.codehere', '.codehere-cache']);
|
|
8
|
+
// Uses the shared walker — same exclusion rules as trust and report.
|
|
15
9
|
export function collectFilesForMCP(target) {
|
|
10
|
+
const paths = collectCodeFiles(target);
|
|
16
11
|
const result = [];
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
walk(full);
|
|
24
|
-
}
|
|
25
|
-
else if (CODE_EXTENSIONS.has(extname(entry.name))) {
|
|
26
|
-
try {
|
|
27
|
-
result.push({ filepath: full, code: readFileSync(full, 'utf-8') });
|
|
28
|
-
}
|
|
29
|
-
catch {
|
|
30
|
-
// skip unreadable files
|
|
31
|
-
}
|
|
32
|
-
}
|
|
12
|
+
for (const filepath of paths) {
|
|
13
|
+
try {
|
|
14
|
+
result.push({ filepath, code: readFileSync(filepath, 'utf-8') });
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
// skip unreadable files (iCloud-evicted, permissions, too large)
|
|
33
18
|
}
|
|
34
19
|
}
|
|
35
|
-
const stat = statSync(target);
|
|
36
|
-
if (stat.isFile()) {
|
|
37
|
-
return [{ filepath: target, code: readFileSync(target, 'utf-8') }];
|
|
38
|
-
}
|
|
39
|
-
walk(target);
|
|
40
20
|
return result;
|
|
41
21
|
}
|
|
42
22
|
// ── Scoring ──────────────────────────────────────────────────────────────────
|