@sanqianx/ai-coding-event-bridge-console 0.7.2 → 0.7.4

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 (36) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +81 -81
  3. package/console/index.html +953 -953
  4. package/package.json +26 -26
  5. package/src/agent-detect.js +128 -128
  6. package/src/bridge/bin/commit-boundary.js +64 -64
  7. package/src/bridge/connectors/capture-guard.js +24 -24
  8. package/src/bridge/connectors/claude-code/hook-entry.js +45 -45
  9. package/src/bridge/connectors/claude-code/notify.js +57 -57
  10. package/src/bridge/connectors/codex/cursor-store.js +83 -83
  11. package/src/bridge/connectors/codex/hook-entry.js +215 -215
  12. package/src/bridge/connectors/codex/session-parser.js +100 -100
  13. package/src/bridge/connectors/opencode/hook-entry.js +55 -55
  14. package/src/bridge/core/bridge.js +249 -246
  15. package/src/bridge/core/commit-projection.js +288 -288
  16. package/src/bridge/core/compaction.js +23 -23
  17. package/src/bridge/core/consumer-registry.js +111 -111
  18. package/src/bridge/core/event-schema.js +127 -127
  19. package/src/bridge/core/fs-utils.js +81 -81
  20. package/src/bridge/core/journal-router.js +39 -39
  21. package/src/bridge/core/journal.js +507 -507
  22. package/src/bridge/core/lock.js +96 -96
  23. package/src/bridge/core/normalizer.js +114 -114
  24. package/src/bridge/core/paths.js +12 -12
  25. package/src/bridge/core/project-registry.js +176 -176
  26. package/src/bridge/core/repo-context.js +191 -191
  27. package/src/bridge/core/runtime-home.js +189 -189
  28. package/src/bridge/core/semver.js +23 -23
  29. package/src/bridge/core/turn-identity.js +127 -127
  30. package/src/bridge/index.js +86 -86
  31. package/src/bridge/installers/claude-code/installer.js +181 -181
  32. package/src/bridge/installers/codex/installer.js +114 -114
  33. package/src/bridge/installers/opencode/installer.js +96 -96
  34. package/src/bridge/installers/zcode/installer.js +200 -200
  35. package/src/bridge/query/conversation-query.js +194 -194
  36. 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
+ };