@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.
- package/LICENSE +21 -0
- package/README.md +173 -0
- package/bin/synapse +0 -0
- package/cmd/rubriceval/main.go +308 -0
- package/cmd/synapse/main.go +280 -0
- package/cmd/synapse/main_test.go +16 -0
- package/go.mod +7 -0
- package/go.sum +6 -0
- package/internal/adapter/adapter.go +1117 -0
- package/internal/adapter/adapter_test.go +518 -0
- package/internal/adapter/agy/adapter.go +193 -0
- package/internal/adapter/claudecode/adapter.go +415 -0
- package/internal/adapter/claudecode/adapter_test.go +260 -0
- package/internal/adapter/claudecode/agents.go +387 -0
- package/internal/adapter/claudecode/agents_test.go +480 -0
- package/internal/adapter/claudecode/summary_inputs_test.go +19 -0
- package/internal/adapter/codex/adapter.go +919 -0
- package/internal/adapter/codex/adapter_test.go +920 -0
- package/internal/adapter/codex/agents.go +401 -0
- package/internal/adapter/codex/agents_test.go +610 -0
- package/internal/adapter/codex/summary_inputs_test.go +20 -0
- package/internal/adapter/pi/adapter.go +483 -0
- package/internal/adapter/pi/adapter_test.go +517 -0
- package/internal/citymap/builder.go +1124 -0
- package/internal/citymap/builder_test.go +818 -0
- package/internal/judge/cache.go +180 -0
- package/internal/judge/cli.go +240 -0
- package/internal/judge/cli_test.go +68 -0
- package/internal/judge/fresh_summary_test.go +37 -0
- package/internal/judge/input.go +233 -0
- package/internal/judge/judge.go +416 -0
- package/internal/judge/judge_test.go +288 -0
- package/internal/judge/prompt.go +129 -0
- package/internal/judge/rubric.go +275 -0
- package/internal/judge/rubric_test.go +642 -0
- package/internal/model/agent.go +63 -0
- package/internal/model/agent_schema_test.go +86 -0
- package/internal/model/agent_test.go +64 -0
- package/internal/model/model.go +175 -0
- package/internal/model/report.go +166 -0
- package/internal/model/stats.go +151 -0
- package/internal/model/stats_test.go +89 -0
- package/internal/model/trace_schema_test.go +67 -0
- package/internal/server/analyze.go +286 -0
- package/internal/server/analyze_test.go +297 -0
- package/internal/server/codex_index_test.go +40 -0
- package/internal/server/hardening_test.go +147 -0
- package/internal/server/reportindex.go +91 -0
- package/internal/server/reportindex_test.go +116 -0
- package/internal/server/server.go +1099 -0
- package/internal/server/server_test.go +1389 -0
- package/internal/server/static/assets/fraunces-latin-ext-standard-italic-CGbN9UgK.woff2 +0 -0
- package/internal/server/static/assets/fraunces-latin-ext-standard-normal-CJcjJNj7.woff2 +0 -0
- package/internal/server/static/assets/fraunces-latin-standard-italic-lSdLDfvT.woff2 +0 -0
- package/internal/server/static/assets/fraunces-latin-standard-normal-DihXLNYH.woff2 +0 -0
- package/internal/server/static/assets/fraunces-vietnamese-standard-italic-DxWqP7Ku.woff2 +0 -0
- package/internal/server/static/assets/fraunces-vietnamese-standard-normal-Czevyj-6.woff2 +0 -0
- package/internal/server/static/assets/index-BNoY_BiB.css +1 -0
- package/internal/server/static/assets/index-C_adLrJr.js +3 -0
- package/internal/server/static/assets/react-gcHzaSmV.js +10 -0
- package/internal/server/static/assets/schibsted-grotesk-latin-ext-wght-normal-hsMS0n0O.woff2 +0 -0
- package/internal/server/static/assets/schibsted-grotesk-latin-wght-normal-Bb8VGrTG.woff2 +0 -0
- package/internal/server/static/assets/three-DnGjZfD1.js +4012 -0
- package/internal/server/static/index.html +26 -0
- package/internal/server/tracestore.go +173 -0
- package/internal/server/tracestore_test.go +51 -0
- package/internal/textutil/truncate.go +30 -0
- package/internal/textutil/truncate_test.go +39 -0
- package/package.json +35 -0
- package/web/e2e/agent-lens.spec.ts +688 -0
- package/web/index.html +23 -0
- package/web/package-lock.json +1933 -0
- package/web/package.json +33 -0
- package/web/playwright.config.ts +24 -0
- package/web/src/App.tsx +876 -0
- package/web/src/api/client.ts +74 -0
- package/web/src/main.tsx +12 -0
- package/web/src/playback/recorder.ts +160 -0
- package/web/src/playback/reducer.ts +91 -0
- package/web/src/scene/CityScene.tsx +638 -0
- package/web/src/scene/TreeScene.tsx +656 -0
- package/web/src/scene/dirLabels.ts +145 -0
- package/web/src/scene/sceneUtils.ts +144 -0
- package/web/src/scene/textures.ts +60 -0
- package/web/src/scene/trail.ts +79 -0
- package/web/src/scene/treeLayout.ts +169 -0
- package/web/src/state/filters.ts +40 -0
- package/web/src/state/store.ts +83 -0
- package/web/src/styles.css +2565 -0
- package/web/src/types.ts +315 -0
- package/web/src/ui/AgentsPanel.tsx +376 -0
- package/web/src/ui/Dock.tsx +104 -0
- package/web/src/ui/Hud.tsx +335 -0
- package/web/src/ui/Inspector.tsx +107 -0
- package/web/src/ui/LogoMark.tsx +38 -0
- package/web/src/ui/ReportPanel.tsx +491 -0
- package/web/src/ui/SessionRail.tsx +316 -0
- package/web/src/ui/Timeline.tsx +458 -0
- package/web/src/ui/ViewPanel.tsx +45 -0
- package/web/src/ui/shortcuts.ts +4 -0
- package/web/tsconfig.json +21 -0
- 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
|
+
}
|