@hybridlabor-api/bdb-synapse 1.0.0

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 (102) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +173 -0
  3. package/bin/synapse +0 -0
  4. package/cmd/rubriceval/main.go +308 -0
  5. package/cmd/synapse/main.go +280 -0
  6. package/cmd/synapse/main_test.go +16 -0
  7. package/go.mod +7 -0
  8. package/go.sum +6 -0
  9. package/internal/adapter/adapter.go +1117 -0
  10. package/internal/adapter/adapter_test.go +518 -0
  11. package/internal/adapter/agy/adapter.go +193 -0
  12. package/internal/adapter/claudecode/adapter.go +415 -0
  13. package/internal/adapter/claudecode/adapter_test.go +260 -0
  14. package/internal/adapter/claudecode/agents.go +387 -0
  15. package/internal/adapter/claudecode/agents_test.go +480 -0
  16. package/internal/adapter/claudecode/summary_inputs_test.go +19 -0
  17. package/internal/adapter/codex/adapter.go +919 -0
  18. package/internal/adapter/codex/adapter_test.go +920 -0
  19. package/internal/adapter/codex/agents.go +401 -0
  20. package/internal/adapter/codex/agents_test.go +610 -0
  21. package/internal/adapter/codex/summary_inputs_test.go +20 -0
  22. package/internal/adapter/pi/adapter.go +483 -0
  23. package/internal/adapter/pi/adapter_test.go +517 -0
  24. package/internal/citymap/builder.go +1124 -0
  25. package/internal/citymap/builder_test.go +818 -0
  26. package/internal/judge/cache.go +180 -0
  27. package/internal/judge/cli.go +240 -0
  28. package/internal/judge/cli_test.go +68 -0
  29. package/internal/judge/fresh_summary_test.go +37 -0
  30. package/internal/judge/input.go +233 -0
  31. package/internal/judge/judge.go +416 -0
  32. package/internal/judge/judge_test.go +288 -0
  33. package/internal/judge/prompt.go +129 -0
  34. package/internal/judge/rubric.go +275 -0
  35. package/internal/judge/rubric_test.go +642 -0
  36. package/internal/model/agent.go +63 -0
  37. package/internal/model/agent_schema_test.go +86 -0
  38. package/internal/model/agent_test.go +64 -0
  39. package/internal/model/model.go +175 -0
  40. package/internal/model/report.go +166 -0
  41. package/internal/model/stats.go +151 -0
  42. package/internal/model/stats_test.go +89 -0
  43. package/internal/model/trace_schema_test.go +67 -0
  44. package/internal/server/analyze.go +286 -0
  45. package/internal/server/analyze_test.go +297 -0
  46. package/internal/server/codex_index_test.go +40 -0
  47. package/internal/server/hardening_test.go +147 -0
  48. package/internal/server/reportindex.go +91 -0
  49. package/internal/server/reportindex_test.go +116 -0
  50. package/internal/server/server.go +1099 -0
  51. package/internal/server/server_test.go +1389 -0
  52. package/internal/server/static/assets/fraunces-latin-ext-standard-italic-CGbN9UgK.woff2 +0 -0
  53. package/internal/server/static/assets/fraunces-latin-ext-standard-normal-CJcjJNj7.woff2 +0 -0
  54. package/internal/server/static/assets/fraunces-latin-standard-italic-lSdLDfvT.woff2 +0 -0
  55. package/internal/server/static/assets/fraunces-latin-standard-normal-DihXLNYH.woff2 +0 -0
  56. package/internal/server/static/assets/fraunces-vietnamese-standard-italic-DxWqP7Ku.woff2 +0 -0
  57. package/internal/server/static/assets/fraunces-vietnamese-standard-normal-Czevyj-6.woff2 +0 -0
  58. package/internal/server/static/assets/index-BNoY_BiB.css +1 -0
  59. package/internal/server/static/assets/index-C_adLrJr.js +3 -0
  60. package/internal/server/static/assets/react-gcHzaSmV.js +10 -0
  61. package/internal/server/static/assets/schibsted-grotesk-latin-ext-wght-normal-hsMS0n0O.woff2 +0 -0
  62. package/internal/server/static/assets/schibsted-grotesk-latin-wght-normal-Bb8VGrTG.woff2 +0 -0
  63. package/internal/server/static/assets/three-DnGjZfD1.js +4012 -0
  64. package/internal/server/static/index.html +26 -0
  65. package/internal/server/tracestore.go +173 -0
  66. package/internal/server/tracestore_test.go +51 -0
  67. package/internal/textutil/truncate.go +30 -0
  68. package/internal/textutil/truncate_test.go +39 -0
  69. package/package.json +35 -0
  70. package/web/e2e/agent-lens.spec.ts +688 -0
  71. package/web/index.html +23 -0
  72. package/web/package-lock.json +1933 -0
  73. package/web/package.json +33 -0
  74. package/web/playwright.config.ts +24 -0
  75. package/web/src/App.tsx +876 -0
  76. package/web/src/api/client.ts +74 -0
  77. package/web/src/main.tsx +12 -0
  78. package/web/src/playback/recorder.ts +160 -0
  79. package/web/src/playback/reducer.ts +91 -0
  80. package/web/src/scene/CityScene.tsx +638 -0
  81. package/web/src/scene/TreeScene.tsx +656 -0
  82. package/web/src/scene/dirLabels.ts +145 -0
  83. package/web/src/scene/sceneUtils.ts +144 -0
  84. package/web/src/scene/textures.ts +60 -0
  85. package/web/src/scene/trail.ts +79 -0
  86. package/web/src/scene/treeLayout.ts +169 -0
  87. package/web/src/state/filters.ts +40 -0
  88. package/web/src/state/store.ts +83 -0
  89. package/web/src/styles.css +2565 -0
  90. package/web/src/types.ts +315 -0
  91. package/web/src/ui/AgentsPanel.tsx +376 -0
  92. package/web/src/ui/Dock.tsx +104 -0
  93. package/web/src/ui/Hud.tsx +335 -0
  94. package/web/src/ui/Inspector.tsx +107 -0
  95. package/web/src/ui/LogoMark.tsx +38 -0
  96. package/web/src/ui/ReportPanel.tsx +491 -0
  97. package/web/src/ui/SessionRail.tsx +316 -0
  98. package/web/src/ui/Timeline.tsx +458 -0
  99. package/web/src/ui/ViewPanel.tsx +45 -0
  100. package/web/src/ui/shortcuts.ts +4 -0
  101. package/web/tsconfig.json +21 -0
  102. package/web/vite.config.ts +28 -0
@@ -0,0 +1,116 @@
1
+ package server
2
+
3
+ import (
4
+ "encoding/json"
5
+ "net/http"
6
+ "net/http/httptest"
7
+ "path/filepath"
8
+ "testing"
9
+
10
+ "github.com/hybridlabor-api/bdb-synapse/internal/judge"
11
+ "github.com/hybridlabor-api/bdb-synapse/internal/model"
12
+ )
13
+
14
+ func validReport(eventCount, userTurns int, digest string) *model.Report {
15
+ return &model.Report{
16
+ Version: 1,
17
+ Session: model.ReportSession{ID: "s", EventCount: eventCount, UserTurns: userTurns},
18
+ Judge: model.ReportJudge{CLI: "stub", PromptVersion: judge.PromptVersion, InputDigest: digest},
19
+ Dimensions: []model.ReportDimension{{Name: "exploration", Findings: []model.ReportFinding{}}},
20
+ }
21
+ }
22
+
23
+ func TestReportIndexMemoizesAndTracksFileChanges(t *testing.T) {
24
+ cache := judge.Cache{Dir: t.TempDir()}
25
+ if err := cache.Store("key1", validReport(3, 1, "digest-one")); err != nil {
26
+ t.Fatal(err)
27
+ }
28
+ ri := &reportIndex{}
29
+ first := ri.load(cache, "key1")
30
+ if first == nil {
31
+ t.Fatal("stored report not found")
32
+ }
33
+ if second := ri.load(cache, "key1"); second != first {
34
+ t.Fatal("unchanged file was re-read instead of memoized")
35
+ }
36
+ if ri.load(cache, "absent") != nil {
37
+ t.Fatal("absent key returned a report")
38
+ }
39
+
40
+ if err := cache.Store("key1", validReport(3, 1, "digest-two-changed")); err != nil {
41
+ t.Fatal(err)
42
+ }
43
+ third := ri.load(cache, "key1")
44
+ if third == nil || third == first || third.Judge.InputDigest != "digest-two-changed" {
45
+ t.Fatalf("changed file not reloaded: %+v", third)
46
+ }
47
+ }
48
+
49
+ func TestReportIndexMarkPresentBeatsListingTTL(t *testing.T) {
50
+ cache := judge.Cache{Dir: t.TempDir()}
51
+ ri := &reportIndex{}
52
+ if ri.load(cache, "k") != nil {
53
+ t.Fatal("empty dir returned a report")
54
+ }
55
+ // The listing is now warm and won't rescan for reportIndexTTL: a report
56
+ // stored by this process must be pushed into the index, not discovered.
57
+ if err := cache.Store("k", validReport(1, 1, "d")); err != nil {
58
+ t.Fatal(err)
59
+ }
60
+ if ri.load(cache, "k") != nil {
61
+ t.Fatal("listing rescanned within TTL; markPresent has nothing to fix")
62
+ }
63
+ ri.markPresent("k")
64
+ if ri.load(cache, "k") == nil {
65
+ t.Fatal("markPresent did not surface the stored report")
66
+ }
67
+ }
68
+
69
+ func TestSessionListReportStatesComeFromIndex(t *testing.T) {
70
+ claudeDir := t.TempDir()
71
+ for _, name := range []string{"scored", "staled", "bare"} {
72
+ writeServerSession(t, filepath.Join(claudeDir, name+".jsonl"),
73
+ `{"type":"user","timestamp":"2026-07-09T00:00:00Z","sessionId":"`+name+`","cwd":"/tmp","message":{"role":"user","content":"do"}}`,
74
+ `{"type":"assistant","timestamp":"2026-07-09T00:00:01Z","sessionId":"`+name+`","cwd":"/tmp","message":{"role":"assistant","content":[{"type":"tool_use","id":"r1","name":"Read","input":{"file_path":"/tmp/a.go"}}]}}`,
75
+ `{"type":"user","timestamp":"2026-07-09T00:00:02Z","sessionId":"`+name+`","cwd":"/tmp","message":{"role":"user","content":[{"type":"tool_result","tool_use_id":"r1","content":"ok","is_error":false}]}}`,
76
+ )
77
+ }
78
+ reportsDir := t.TempDir()
79
+
80
+ // Seed reports before the server exists so its first listing sees them.
81
+ seed := New(Config{ClaudeDir: claudeDir, CodexDir: filepath.Join(t.TempDir(), "codex")})
82
+ seed.reportCache.Dir = reportsDir
83
+ scored, err := seed.findSession("scored")
84
+ if err != nil {
85
+ t.Fatal(err)
86
+ }
87
+ staled, err := seed.findSession("staled")
88
+ if err != nil {
89
+ t.Fatal(err)
90
+ }
91
+ if err := seed.reportCache.Store(scored.Key, validReport(scored.EventCount, scored.UserTurns, "d")); err != nil {
92
+ t.Fatal(err)
93
+ }
94
+ if err := seed.reportCache.Store(staled.Key, validReport(staled.EventCount+1, staled.UserTurns, "d")); err != nil {
95
+ t.Fatal(err)
96
+ }
97
+
98
+ s := New(Config{ClaudeDir: claudeDir, CodexDir: filepath.Join(t.TempDir(), "codex")})
99
+ s.reportCache.Dir = reportsDir
100
+ resp := httptest.NewRecorder()
101
+ s.handleSessions(resp, httptest.NewRequest(http.MethodGet, "/api/sessions", nil))
102
+ if resp.Code != http.StatusOK {
103
+ t.Fatalf("sessions status = %d", resp.Code)
104
+ }
105
+ var items []sessionListItem
106
+ if err := json.Unmarshal(resp.Body.Bytes(), &items); err != nil {
107
+ t.Fatal(err)
108
+ }
109
+ states := map[string]string{}
110
+ for _, item := range items {
111
+ states[item.ID] = item.ReportState
112
+ }
113
+ if states["scored"] != "done" || states["staled"] != "stale" || states["bare"] != "" {
114
+ t.Fatalf("states = %#v", states)
115
+ }
116
+ }