@sanqianx/ai-coding-event-bridge-console 0.7.1 → 0.7.3

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.
Files changed (37) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +81 -74
  3. package/console/index.html +953 -948
  4. package/package.json +26 -26
  5. package/src/agent-detect.js +128 -128
  6. package/src/bin.js +80 -80
  7. package/src/bridge/bin/commit-boundary.js +64 -64
  8. package/src/bridge/connectors/capture-guard.js +24 -24
  9. package/src/bridge/connectors/claude-code/hook-entry.js +45 -45
  10. package/src/bridge/connectors/claude-code/notify.js +57 -57
  11. package/src/bridge/connectors/codex/cursor-store.js +83 -83
  12. package/src/bridge/connectors/codex/hook-entry.js +215 -215
  13. package/src/bridge/connectors/codex/session-parser.js +100 -100
  14. package/src/bridge/connectors/opencode/hook-entry.js +55 -55
  15. package/src/bridge/core/bridge.js +249 -246
  16. package/src/bridge/core/commit-projection.js +288 -288
  17. package/src/bridge/core/compaction.js +23 -23
  18. package/src/bridge/core/consumer-registry.js +111 -111
  19. package/src/bridge/core/event-schema.js +127 -127
  20. package/src/bridge/core/fs-utils.js +81 -81
  21. package/src/bridge/core/journal-router.js +39 -39
  22. package/src/bridge/core/journal.js +507 -507
  23. package/src/bridge/core/lock.js +96 -96
  24. package/src/bridge/core/normalizer.js +114 -114
  25. package/src/bridge/core/paths.js +12 -12
  26. package/src/bridge/core/project-registry.js +176 -176
  27. package/src/bridge/core/repo-context.js +191 -191
  28. package/src/bridge/core/runtime-home.js +189 -188
  29. package/src/bridge/core/semver.js +23 -23
  30. package/src/bridge/core/turn-identity.js +127 -127
  31. package/src/bridge/index.js +86 -86
  32. package/src/bridge/installers/claude-code/installer.js +181 -181
  33. package/src/bridge/installers/codex/installer.js +114 -114
  34. package/src/bridge/installers/opencode/installer.js +96 -96
  35. package/src/bridge/installers/zcode/installer.js +200 -188
  36. package/src/bridge/query/conversation-query.js +194 -194
  37. package/src/server.js +825 -825
@@ -1,127 +1,127 @@
1
- 'use strict';
2
-
3
- // F-024: DevTask-Radar used to synthesize '(No captured user prompt for this
4
- // turn)' as fake user text. Synthetic evidence is forbidden anywhere downstream.
5
- const SYNTHETIC_PROMPT_MARKERS = [
6
- '(no captured user prompt for this turn)',
7
- '(no user prompt captured)',
8
- 'unknown-session'
9
- ];
10
-
11
- function isSyntheticPrompt(content) {
12
- if (typeof content !== 'string') return false;
13
- const trimmed = content.trim().toLowerCase();
14
- return SYNTHETIC_PROMPT_MARKERS.includes(trimmed);
15
- }
16
-
17
- function orderBySequence(events) {
18
- return [...events].sort((a, b) => {
19
- if (a.sequence !== b.sequence) return a.sequence - b.sequence;
20
- return String(a.eventId || '').localeCompare(String(b.eventId || ''));
21
- });
22
- }
23
-
24
- function isDuplicateEvent(a, b) {
25
- if (!a || !b) return false;
26
- if (a.eventKey && b.eventKey && a.eventKey === b.eventKey) return true;
27
- return (
28
- a.source === b.source &&
29
- a.eventType === b.eventType &&
30
- (a.sessionId || null) === (b.sessionId || null) &&
31
- (a.turnId || null) === (b.turnId || null) &&
32
- a.content === b.content &&
33
- a.eventId !== undefined &&
34
- a.eventId === b.eventId
35
- );
36
- }
37
-
38
- /**
39
- * Deterministic turn projection over ordered events. Mirrors the journal's
40
- * durable projection: user_prompt opens a turn; assistant_response closes the
41
- * matching open turn (by turnId, or by sessionId when the client did not
42
- * provide a turn id). Assistant events that cannot be tied to an open turn are
43
- * orphan evidence — they are kept, never turned into fake prompts.
44
- */
45
- function deriveTurnState(events) {
46
- const ordered = orderBySequence(events.filter((e) => e && typeof e === 'object'));
47
- const openTurns = new Map();
48
- const closedTurns = new Map();
49
- const orphanAssistantEvents = [];
50
-
51
- const findOpenTurnForAssistant = (event) => {
52
- if (event.turnId && openTurns.has(event.turnId)) return openTurns.get(event.turnId);
53
- if (event.turnId) return null;
54
- if (!event.sessionId) return null;
55
- const matches = [...openTurns.values()].filter((t) => t.sessionId === event.sessionId);
56
- if (matches.length === 1) return matches[0];
57
- return null;
58
- };
59
-
60
- for (const event of ordered) {
61
- if (event.eventType === 'user_prompt') {
62
- if (!event.turnId) continue; // unverifiable prompt: identity layer marks it, no fake turn
63
- if (openTurns.has(event.turnId) || closedTurns.has(event.turnId)) {
64
- const turn = openTurns.get(event.turnId) || closedTurns.get(event.turnId);
65
- turn.userEvents.push(event);
66
- continue;
67
- }
68
- openTurns.set(event.turnId, {
69
- turnId: event.turnId,
70
- sessionId: event.sessionId || null,
71
- source: event.source,
72
- startSequence: event.sequence,
73
- endSequence: null,
74
- userEvents: [event],
75
- assistantEvents: []
76
- });
77
- continue;
78
- }
79
- if (event.eventType === 'assistant_response') {
80
- const turn = findOpenTurnForAssistant(event);
81
- if (!turn) {
82
- orphanAssistantEvents.push(event);
83
- continue;
84
- }
85
- turn.assistantEvents.push(event);
86
- turn.endSequence = event.sequence;
87
- openTurns.delete(turn.turnId);
88
- closedTurns.set(turn.turnId, turn);
89
- continue;
90
- }
91
- if (event.eventType === 'session_end' && event.sessionId) {
92
- for (const turn of [...openTurns.values()]) {
93
- if (turn.sessionId === event.sessionId) {
94
- turn.endSequence = event.sequence;
95
- openTurns.delete(turn.turnId);
96
- closedTurns.set(turn.turnId, turn);
97
- }
98
- }
99
- }
100
- }
101
-
102
- return {
103
- openTurns: [...openTurns.values()],
104
- closedTurns: [...closedTurns.values()],
105
- orphanAssistantEvents
106
- };
107
- }
108
-
109
- function assertNoSyntheticEvidence(events) {
110
- for (const event of events) {
111
- if (event && event.role === 'user' && isSyntheticPrompt(event.content)) {
112
- throw new Error(`synthetic user prompt is forbidden: ${String(event.content).slice(0, 60)}`);
113
- }
114
- if (event && typeof event.sessionId === 'string' && event.sessionId.toLowerCase() === 'unknown-session') {
115
- throw new Error('unknown-session is forbidden');
116
- }
117
- }
118
- return true;
119
- }
120
-
121
- module.exports = {
122
- isSyntheticPrompt,
123
- orderBySequence,
124
- isDuplicateEvent,
125
- deriveTurnState,
126
- assertNoSyntheticEvidence
127
- };
1
+ 'use strict';
2
+
3
+ // F-024: DevTask-Radar used to synthesize '(No captured user prompt for this
4
+ // turn)' as fake user text. Synthetic evidence is forbidden anywhere downstream.
5
+ const SYNTHETIC_PROMPT_MARKERS = [
6
+ '(no captured user prompt for this turn)',
7
+ '(no user prompt captured)',
8
+ 'unknown-session'
9
+ ];
10
+
11
+ function isSyntheticPrompt(content) {
12
+ if (typeof content !== 'string') return false;
13
+ const trimmed = content.trim().toLowerCase();
14
+ return SYNTHETIC_PROMPT_MARKERS.includes(trimmed);
15
+ }
16
+
17
+ function orderBySequence(events) {
18
+ return [...events].sort((a, b) => {
19
+ if (a.sequence !== b.sequence) return a.sequence - b.sequence;
20
+ return String(a.eventId || '').localeCompare(String(b.eventId || ''));
21
+ });
22
+ }
23
+
24
+ function isDuplicateEvent(a, b) {
25
+ if (!a || !b) return false;
26
+ if (a.eventKey && b.eventKey && a.eventKey === b.eventKey) return true;
27
+ return (
28
+ a.source === b.source &&
29
+ a.eventType === b.eventType &&
30
+ (a.sessionId || null) === (b.sessionId || null) &&
31
+ (a.turnId || null) === (b.turnId || null) &&
32
+ a.content === b.content &&
33
+ a.eventId !== undefined &&
34
+ a.eventId === b.eventId
35
+ );
36
+ }
37
+
38
+ /**
39
+ * Deterministic turn projection over ordered events. Mirrors the journal's
40
+ * durable projection: user_prompt opens a turn; assistant_response closes the
41
+ * matching open turn (by turnId, or by sessionId when the client did not
42
+ * provide a turn id). Assistant events that cannot be tied to an open turn are
43
+ * orphan evidence — they are kept, never turned into fake prompts.
44
+ */
45
+ function deriveTurnState(events) {
46
+ const ordered = orderBySequence(events.filter((e) => e && typeof e === 'object'));
47
+ const openTurns = new Map();
48
+ const closedTurns = new Map();
49
+ const orphanAssistantEvents = [];
50
+
51
+ const findOpenTurnForAssistant = (event) => {
52
+ if (event.turnId && openTurns.has(event.turnId)) return openTurns.get(event.turnId);
53
+ if (event.turnId) return null;
54
+ if (!event.sessionId) return null;
55
+ const matches = [...openTurns.values()].filter((t) => t.sessionId === event.sessionId);
56
+ if (matches.length === 1) return matches[0];
57
+ return null;
58
+ };
59
+
60
+ for (const event of ordered) {
61
+ if (event.eventType === 'user_prompt') {
62
+ if (!event.turnId) continue; // unverifiable prompt: identity layer marks it, no fake turn
63
+ if (openTurns.has(event.turnId) || closedTurns.has(event.turnId)) {
64
+ const turn = openTurns.get(event.turnId) || closedTurns.get(event.turnId);
65
+ turn.userEvents.push(event);
66
+ continue;
67
+ }
68
+ openTurns.set(event.turnId, {
69
+ turnId: event.turnId,
70
+ sessionId: event.sessionId || null,
71
+ source: event.source,
72
+ startSequence: event.sequence,
73
+ endSequence: null,
74
+ userEvents: [event],
75
+ assistantEvents: []
76
+ });
77
+ continue;
78
+ }
79
+ if (event.eventType === 'assistant_response') {
80
+ const turn = findOpenTurnForAssistant(event);
81
+ if (!turn) {
82
+ orphanAssistantEvents.push(event);
83
+ continue;
84
+ }
85
+ turn.assistantEvents.push(event);
86
+ turn.endSequence = event.sequence;
87
+ openTurns.delete(turn.turnId);
88
+ closedTurns.set(turn.turnId, turn);
89
+ continue;
90
+ }
91
+ if (event.eventType === 'session_end' && event.sessionId) {
92
+ for (const turn of [...openTurns.values()]) {
93
+ if (turn.sessionId === event.sessionId) {
94
+ turn.endSequence = event.sequence;
95
+ openTurns.delete(turn.turnId);
96
+ closedTurns.set(turn.turnId, turn);
97
+ }
98
+ }
99
+ }
100
+ }
101
+
102
+ return {
103
+ openTurns: [...openTurns.values()],
104
+ closedTurns: [...closedTurns.values()],
105
+ orphanAssistantEvents
106
+ };
107
+ }
108
+
109
+ function assertNoSyntheticEvidence(events) {
110
+ for (const event of events) {
111
+ if (event && event.role === 'user' && isSyntheticPrompt(event.content)) {
112
+ throw new Error(`synthetic user prompt is forbidden: ${String(event.content).slice(0, 60)}`);
113
+ }
114
+ if (event && typeof event.sessionId === 'string' && event.sessionId.toLowerCase() === 'unknown-session') {
115
+ throw new Error('unknown-session is forbidden');
116
+ }
117
+ }
118
+ return true;
119
+ }
120
+
121
+ module.exports = {
122
+ isSyntheticPrompt,
123
+ orderBySequence,
124
+ isDuplicateEvent,
125
+ deriveTurnState,
126
+ assertNoSyntheticEvidence
127
+ };
@@ -1,86 +1,86 @@
1
- 'use strict';
2
-
3
- const packageInfo = require('../../package.json');
4
- const { bridgeHome } = require('./core/paths');
5
- const { Journal, JournalValidationError, JournalCorruptError, EVENT_SCHEMA, BOUNDARY_SCHEMA } = require('./core/journal');
6
- const { ConsumerRegistry } = require('./core/consumer-registry');
7
- const { compactJournal } = require('./core/compaction');
8
- const { ensureRuntimeHome } = require('./core/runtime-home');
9
- const semver = require('./core/semver');
10
- const { validateAndNormalizeEvent, EventSchemaError } = require('./core/event-schema');
11
- const {
12
- normalizeGitUrl,
13
- resolveRepoContext,
14
- REPO_IDENTITY_SCHEMA,
15
- buildRepoIdentityV1,
16
- isValidRepoIdentityV1,
17
- repoIdentityKey,
18
- sameRepoIdentity,
19
- workspaceIdFor
20
- } = require('./core/repo-context');
21
- const {
22
- isSyntheticPrompt,
23
- orderBySequence,
24
- isDuplicateEvent,
25
- deriveTurnState,
26
- assertNoSyntheticEvidence
27
- } = require('./core/turn-identity');
28
- const { normalizeClaudeCode, normalizeCodex, normalizeOpenCode } = require('./core/normalizer');
29
- const projectRegistry = require('./core/project-registry');
30
- const commitProjection = require('./core/commit-projection');
31
- const { journalFor, globalJournalDir, projectJournalDir } = require('./core/journal-router');
32
- const { ConversationQuery } = require('./query/conversation-query');
33
- const claudeHookEntry = require('./connectors/claude-code/hook-entry');
34
- const claudeInstaller = require('./installers/claude-code/installer');
35
- const codexInstaller = require('./installers/codex/installer');
36
- const openCodeInstaller = require('./installers/opencode/installer');
37
- const zcodeInstaller = require('./installers/zcode/installer');
38
- const { createBridge } = require('./core/bridge');
39
-
40
- module.exports = {
41
- packageName: packageInfo.name,
42
- version: packageInfo.version,
43
- bridgeHome,
44
- Journal,
45
- JournalValidationError,
46
- JournalCorruptError,
47
- EVENT_SCHEMA,
48
- BOUNDARY_SCHEMA,
49
- ConsumerRegistry,
50
- compactJournal,
51
- ensureRuntimeHome,
52
- semver,
53
- validateAndNormalizeEvent,
54
- EventSchemaError,
55
- normalizeGitUrl,
56
- resolveRepoContext,
57
- REPO_IDENTITY_SCHEMA,
58
- buildRepoIdentityV1,
59
- isValidRepoIdentityV1,
60
- repoIdentityKey,
61
- sameRepoIdentity,
62
- workspaceIdFor,
63
- isSyntheticPrompt,
64
- orderBySequence,
65
- isDuplicateEvent,
66
- deriveTurnState,
67
- assertNoSyntheticEvidence,
68
- normalizeClaudeCode,
69
- normalizeCodex,
70
- normalizeOpenCode,
71
- projectRegistry,
72
- commitProjection,
73
- journalFor,
74
- globalJournalDir,
75
- projectJournalDir,
76
- ConversationQuery,
77
- claudeHookEntry,
78
- claudeInstaller,
79
- createBridge,
80
- installers: {
81
- claudeCode: claudeInstaller,
82
- codex: codexInstaller,
83
- openCode: openCodeInstaller,
84
- zcode: zcodeInstaller
85
- }
86
- };
1
+ 'use strict';
2
+
3
+ const packageInfo = require('../../package.json');
4
+ const { bridgeHome } = require('./core/paths');
5
+ const { Journal, JournalValidationError, JournalCorruptError, EVENT_SCHEMA, BOUNDARY_SCHEMA } = require('./core/journal');
6
+ const { ConsumerRegistry } = require('./core/consumer-registry');
7
+ const { compactJournal } = require('./core/compaction');
8
+ const { ensureRuntimeHome } = require('./core/runtime-home');
9
+ const semver = require('./core/semver');
10
+ const { validateAndNormalizeEvent, EventSchemaError } = require('./core/event-schema');
11
+ const {
12
+ normalizeGitUrl,
13
+ resolveRepoContext,
14
+ REPO_IDENTITY_SCHEMA,
15
+ buildRepoIdentityV1,
16
+ isValidRepoIdentityV1,
17
+ repoIdentityKey,
18
+ sameRepoIdentity,
19
+ workspaceIdFor
20
+ } = require('./core/repo-context');
21
+ const {
22
+ isSyntheticPrompt,
23
+ orderBySequence,
24
+ isDuplicateEvent,
25
+ deriveTurnState,
26
+ assertNoSyntheticEvidence
27
+ } = require('./core/turn-identity');
28
+ const { normalizeClaudeCode, normalizeCodex, normalizeOpenCode } = require('./core/normalizer');
29
+ const projectRegistry = require('./core/project-registry');
30
+ const commitProjection = require('./core/commit-projection');
31
+ const { journalFor, globalJournalDir, projectJournalDir } = require('./core/journal-router');
32
+ const { ConversationQuery } = require('./query/conversation-query');
33
+ const claudeHookEntry = require('./connectors/claude-code/hook-entry');
34
+ const claudeInstaller = require('./installers/claude-code/installer');
35
+ const codexInstaller = require('./installers/codex/installer');
36
+ const openCodeInstaller = require('./installers/opencode/installer');
37
+ const zcodeInstaller = require('./installers/zcode/installer');
38
+ const { createBridge } = require('./core/bridge');
39
+
40
+ module.exports = {
41
+ packageName: packageInfo.name,
42
+ version: packageInfo.version,
43
+ bridgeHome,
44
+ Journal,
45
+ JournalValidationError,
46
+ JournalCorruptError,
47
+ EVENT_SCHEMA,
48
+ BOUNDARY_SCHEMA,
49
+ ConsumerRegistry,
50
+ compactJournal,
51
+ ensureRuntimeHome,
52
+ semver,
53
+ validateAndNormalizeEvent,
54
+ EventSchemaError,
55
+ normalizeGitUrl,
56
+ resolveRepoContext,
57
+ REPO_IDENTITY_SCHEMA,
58
+ buildRepoIdentityV1,
59
+ isValidRepoIdentityV1,
60
+ repoIdentityKey,
61
+ sameRepoIdentity,
62
+ workspaceIdFor,
63
+ isSyntheticPrompt,
64
+ orderBySequence,
65
+ isDuplicateEvent,
66
+ deriveTurnState,
67
+ assertNoSyntheticEvidence,
68
+ normalizeClaudeCode,
69
+ normalizeCodex,
70
+ normalizeOpenCode,
71
+ projectRegistry,
72
+ commitProjection,
73
+ journalFor,
74
+ globalJournalDir,
75
+ projectJournalDir,
76
+ ConversationQuery,
77
+ claudeHookEntry,
78
+ claudeInstaller,
79
+ createBridge,
80
+ installers: {
81
+ claudeCode: claudeInstaller,
82
+ codex: codexInstaller,
83
+ openCode: openCodeInstaller,
84
+ zcode: zcodeInstaller
85
+ }
86
+ };