agent-timelines 0.2.0__py3-none-any.whl

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.
@@ -0,0 +1,474 @@
1
+ <!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
2
+ <title>Agent timelines</title><style>
3
+ /* Pixel dungeon. Sprites: Kenney Tiny Dungeon + 1-Bit Pack (CC0); font: Press Start 2P (OFL). See assets/LICENSES.txt. */
4
+ @font-face{font-family:"PS2P";src:url(PressStart2P-Regular.ttf) format("truetype");font-display:swap}
5
+ :root{--bg:#1b1424;--panel:#2a2036;--panel2:#352a45;--edge:#0d0912;--hi:#4b3d60;--fg:#f4ead5;--mut:#a99bb8;--gold:#ffd23f;--bad:#ff5a5a;--ok:#5be37a;--run:#ffd23f;
6
+ --fable:#ff8ad0;--mythos:#c0f060;--opus:#b48cff;--sonnet:#5ab4ff;--haiku:#5be37a;--other:#ff9f43;--wf:#4de0e0}
7
+ @media (prefers-color-scheme:light){:root:not([data-theme=dark]){--bg:#e9d9b8;--panel:#fbf1dc;--panel2:#f1e2c2;--edge:#3b2a1e;--hi:#fffaf0;--fg:#2b1d14;--mut:#7a6450;
8
+ --gold:#9a6a00;--bad:#c62828;--ok:#1f8a3a;--run:#9a6a00;--fable:#b0257a;--mythos:#5a7a00;--opus:#6d3fd6;--sonnet:#1f6feb;--haiku:#1a8a5a;--other:#c25e00;--wf:#00838f}}
9
+ *{box-sizing:border-box}
10
+ html{background:var(--bg)}
11
+ body{margin:0;padding:12px 16px;color:var(--fg);font:10px/1.8 "PS2P",ui-monospace,Menlo,monospace;min-height:100vh;
12
+ /* dungeon wall: offset pixel bricks drawn with gradients */
13
+ background:linear-gradient(var(--panel2) 3px,transparent 3px) 0 0/48px 24px,linear-gradient(90deg,var(--panel2) 3px,transparent 3px) 0 0/48px 24px,
14
+ linear-gradient(90deg,var(--panel2) 3px,transparent 3px) 24px 12px/48px 24px,var(--bg)}
15
+ h1{font-size:14px;margin:0;line-height:1.6}h2{font-size:10px;margin:0 0 8px;color:var(--gold)}h3{font-size:10px;margin:12px 0 6px;color:var(--gold)}
16
+ a{color:var(--sonnet)}.mut{color:var(--mut)}.bad{color:var(--bad)}
17
+ button{font:inherit;color:inherit}
18
+ [data-fam=fable]{--fam:var(--fable)}[data-fam=mythos]{--fam:var(--mythos)}[data-fam=opus]{--fam:var(--opus)}[data-fam=sonnet]{--fam:var(--sonnet)}[data-fam=haiku]{--fam:var(--haiku)}[data-fam=other]{--fam:var(--other)}[data-fam=wf]{--fam:var(--wf)}[data-fam=plan]{--fam:var(--gold)}
19
+ /* chunky pixel frame: hard border + notched corners + hard drop shadow */
20
+ .px{background:var(--panel);border:3px solid var(--edge);box-shadow:inset 3px 3px 0 var(--hi),inset -3px -3px 0 var(--panel2),4px 4px 0 rgba(0,0,0,.45);
21
+ clip-path:polygon(0 3px,3px 3px,3px 0,calc(100% - 3px) 0,calc(100% - 3px) 3px,100% 3px,100% calc(100% - 3px),calc(100% - 3px) calc(100% - 3px),calc(100% - 3px) 100%,3px 100%,3px calc(100% - 3px),0 calc(100% - 3px))}
22
+ /* sprites: --c/--r pick a 16px cell, --s scales it with hard edges */
23
+ .spr{display:inline-block;flex:none;width:calc(16px*var(--s,2));height:calc(16px*var(--s,2));image-rendering:pixelated;vertical-align:middle;
24
+ background-position:calc(var(--c)*-16px*var(--s,2)) calc(var(--r)*-16px*var(--s,2))}
25
+ .td{background-image:url(tiny-dungeon.png);background-size:calc(192px*var(--s,2)) calc(176px*var(--s,2))}
26
+ .ob{background-image:url(one-bit.png);background-size:calc(768px*var(--s,2)) calc(352px*var(--s,2))}
27
+ #hud{padding:10px 14px;margin-bottom:12px}
28
+ [hidden]{display:none!important}
29
+ .btn{color:var(--fg);text-decoration:none;display:inline-block;background:var(--panel2);border:3px solid var(--edge);padding:4px 8px;cursor:pointer;font-size:8px;box-shadow:2px 2px 0 rgba(0,0,0,.4)}
30
+ .btn:hover{background:var(--hi)}.btn[aria-pressed=true]{border-color:var(--gold);color:var(--gold)}
31
+ .btn:active{transform:translate(2px,2px);box-shadow:none}
32
+ .btn:focus-visible,.card:focus-visible,#side button:focus-visible{outline:3px solid var(--gold);outline-offset:2px}
33
+ .stats{display:flex;flex-wrap:wrap;gap:6px 16px;margin:8px 0 4px}.stat{display:inline-flex;align-items:center;gap:6px}.stat b{font-size:12px}
34
+ .meta{margin:2px 0;overflow-wrap:anywhere;font-size:8px}
35
+ .xp{display:flex;height:9px;border:3px solid var(--edge);background:var(--edge);margin:6px 0 2px}.xp span{height:100%;background:var(--fam);transition:width .6s steps(8)}
36
+ .err{color:var(--bad);font-size:8px;margin-top:6px}
37
+ details{margin-top:6px;font-size:8px}summary{cursor:pointer;color:var(--mut)}details dl{display:grid;grid-template-columns:max-content 1fr;gap:6px 12px;margin:8px 0;align-items:center}details dd{margin:0}
38
+ .wrap{display:grid;grid-template-columns:minmax(0,1fr) minmax(240px,330px);gap:14px}
39
+ @media (max-width:820px){.wrap{grid-template-columns:minmax(0,1fr)}}
40
+ #graph{overflow:auto;padding:14px;max-height:calc(100vh - 210px);min-height:280px}
41
+ /* left-to-right party tree in plain CSS: a branch is a card plus a column of child branches; ::before/::after draw pixel-dash paths */
42
+ .br{display:flex;align-items:flex-start;gap:30px;position:relative}
43
+ .kids{display:flex;flex-direction:column;gap:12px;position:relative}
44
+ .kids::before,.kids>.br::before{content:"";position:absolute;top:30px;height:3px;width:15px;background:repeating-linear-gradient(90deg,var(--mut) 0 6px,transparent 6px 9px)}
45
+ .kids::before{left:-30px}.kids>.br::before{left:-15px}
46
+ .kids>.br::after{content:"";position:absolute;left:-15px;top:-12px;width:3px;height:calc(100% + 12px);background:repeating-linear-gradient(180deg,var(--mut) 0 6px,transparent 6px 9px)}
47
+ .kids>.br:first-child::after{top:30px;height:calc(100% - 30px)}
48
+ .kids>.br:last-child::after{height:45px}
49
+ .kids>.br:first-child:last-child::after{display:none}
50
+ .kids>.br.run::before{background:repeating-linear-gradient(90deg,var(--gold) 0 6px,transparent 6px 9px);animation:march .6s steps(3) infinite}
51
+ .card{flex:none;width:262px;padding:8px 10px 9px;cursor:pointer;position:relative;clip-path:none}
52
+ .card[data-st=run]{background:var(--panel2)}
53
+ .card[data-bad]{border-color:var(--bad)}
54
+ .card.sel{outline:3px solid var(--gold);outline-offset:2px}
55
+ .c1{display:flex;gap:8px;align-items:center}
56
+ .who{min-width:0;flex:1}.who b{display:block;color:var(--fam);font-size:10px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
57
+ .tag{display:inline-block;font-size:8px;padding:1px 4px;border:2px solid currentColor;margin-top:2px}
58
+ [data-st=run] .tag{color:var(--run)}[data-st=ok] .tag{color:var(--ok)}[data-st=bad] .tag{color:var(--bad)}[data-st=stop] .tag{color:var(--other)}[data-st=wait] .tag{color:var(--mut)}
59
+ .hero{animation:idle 1.6s steps(1) infinite}[data-st=run] .hero{animation:walk .5s steps(1) infinite}[data-st=bad] .hero{opacity:.75}
60
+ .reply{color:var(--mut)}
61
+ .desc{margin:6px 0 4px;font-size:8px;line-height:1.7;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;overflow-wrap:anywhere}
62
+ .row{display:flex;align-items:center;gap:6px;font-size:8px;margin-top:4px;flex-wrap:wrap}
63
+ .row code{font:inherit;color:var(--mut);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
64
+ .dot{width:8px;height:8px;background:var(--fam);flex:none;box-shadow:0 0 0 2px var(--edge)}
65
+ .hearts{display:inline-flex;gap:1px}.nums{color:var(--mut)}.nums[data-lv=warn]{color:var(--run)}.nums[data-lv=bad]{color:var(--bad)}
66
+ .fl{display:flex;flex-wrap:wrap;gap:4px;margin-top:6px}.fl:empty{display:none}
67
+ .chip{display:inline-flex;align-items:center;gap:3px;font-size:8px;padding:1px 4px;border:2px solid var(--hi);color:var(--mut)}.chip.flag{border-color:var(--bad);color:var(--bad)}.chip.key{border-color:var(--gold);color:var(--gold)}
68
+ /* one-shot effects: a new agent poofs in, a finished one shows a rising +VICTORY */
69
+ .spawn{animation:poof .5s steps(5) both}
70
+ .win::after{content:"+VICTORY";position:absolute;right:8px;top:4px;color:var(--gold);font-size:8px;text-shadow:2px 2px 0 var(--edge);animation:rise 1.6s steps(8) forwards;pointer-events:none}
71
+ #side{padding:10px;display:flex;flex-direction:column;max-height:calc(100vh - 210px);min-height:280px}
72
+ #feed,#list{list-style:none;margin:0;padding:0;overflow:auto;min-height:0}#feed{flex:1}
73
+ #side li{border-bottom:2px dashed var(--panel2)}#side li.new{animation:slide .4s steps(4)}
74
+ #side li>*{display:flex;gap:6px;align-items:flex-start;width:100%;text-align:left;background:none;border:0;padding:5px 2px;font-size:8px;line-height:1.7;overflow-wrap:anywhere}
75
+ #side li>button{cursor:pointer}#side li>button:hover{background:var(--panel2)}
76
+ #feed time{color:var(--mut);flex:none}
77
+ #side details{max-height:40%;display:flex;flex-direction:column}#list{max-height:30vh}
78
+ #panel{position:fixed;z-index:9;top:12px;right:12px;bottom:12px;width:min(420px,calc(100vw - 24px));overflow:auto;padding:16px;animation:slide .3s steps(4)}
79
+ #panel[hidden]{display:none}
80
+ #px{position:absolute;top:6px;right:8px;font-size:14px;background:none;border:0;cursor:pointer;padding:4px 8px;color:var(--mut)}
81
+ .kv{display:flex;gap:8px;margin:4px 0;font-size:8px}.kv b{flex:none;width:96px;color:var(--mut);font-weight:400;overflow-wrap:anywhere}.kv b.bad{color:var(--bad)}.kv span{min-width:0;overflow-wrap:anywhere}
82
+ pre{white-space:pre-wrap;overflow-wrap:anywhere;background:var(--edge);color:#f4ead5;padding:8px;font:11px/1.5 ui-monospace,Menlo,monospace}
83
+ .q{display:inline-grid;place-items:center;flex:none;width:14px;height:14px;padding:0;margin-left:4px;font-size:8px;line-height:1;color:var(--edge);background:var(--mut);
84
+ border:0;box-shadow:inset -2px -2px 0 rgba(0,0,0,.25);cursor:help;vertical-align:middle}.q:hover,.q:focus-visible{background:var(--gold);outline:none}
85
+ #tip{position:fixed;z-index:20;max-width:280px;padding:8px 10px;font-size:8px;line-height:1.8;pointer-events:none}#tip[hidden]{display:none}
86
+ #tip b{color:var(--gold);font-weight:400}
87
+ .views{display:inline-flex;gap:6px;margin-left:12px;vertical-align:middle}
88
+ #mapbox{display:flex;flex-direction:column;padding:8px;max-height:calc(100vh - 210px);min-height:280px}#mapbox[hidden],#graph[hidden]{display:none}
89
+ .mapbar{display:flex;gap:6px;align-items:center;margin-bottom:8px}.mapbar .mut{font-size:8px;min-width:3em;text-align:center}
90
+ #mapscroll{overflow:auto;flex:1;min-height:0;background:var(--edge)}#map{display:block;image-rendering:pixelated;cursor:pointer}
91
+ /* ---- timeline (Gantt) view: one row per agent, one bar per agent on a shared clock */
92
+ #gantt{--lw:180px;display:flex;flex-direction:column;padding:8px;max-height:calc(100vh - 210px);min-height:280px}
93
+ #gscroll{overflow:auto;flex:1;min-height:0}#ginner{position:relative;min-width:100%}
94
+ .gaxis,.grow{display:grid;grid-template-columns:var(--lw) minmax(0,1fr);align-items:center}
95
+ .gaxis{height:22px;font-size:8px;color:var(--mut);border-bottom:3px solid var(--edge)}
96
+ .gaxis .glabel{height:100%;background:var(--panel)}.gaxis .gtrack{height:100%}
97
+ .gtick{position:absolute;top:5px;transform:translateX(-50%);white-space:nowrap}
98
+ .grow{width:100%;height:26px;padding:0;text-align:left;background:none;border:0;border-bottom:2px dashed var(--panel2);cursor:pointer}
99
+ .glabel{position:sticky;left:0;z-index:2;display:flex;align-items:center;gap:4px;height:100%;padding-left:calc(var(--d,0)*10px + 2px);background:var(--panel);color:var(--fam,var(--fg));font-size:8px;overflow:hidden;white-space:nowrap}
100
+ .glabel span{overflow:hidden;text-overflow:ellipsis}
101
+ .grow:hover,.grow:hover .glabel,.grow.sel .glabel{background:var(--panel2)}
102
+ .grow:focus-visible{outline:3px solid var(--gold);outline-offset:-3px}
103
+ .gtrack{position:relative;height:100%;margin-right:36px}
104
+ .gbar{position:absolute;top:7px;height:12px;min-width:4px;background:var(--fam);box-shadow:0 0 0 2px var(--edge)}
105
+ .gbar.run{background:repeating-linear-gradient(90deg,var(--fam) 0 6px,var(--gold) 6px 9px);animation:march .6s steps(3) infinite}
106
+ .grow[data-st=bad] .gbar{box-shadow:0 0 0 2px var(--bad)}
107
+ .grow.sel .gbar{outline:3px solid var(--gold);outline-offset:1px}
108
+ .gbar .spr{position:absolute;left:100%;top:-2px;margin-left:4px}.gbar .spr+.spr{margin-left:22px}
109
+ #gcols{position:absolute;top:0;bottom:0;left:var(--lw);right:36px;pointer-events:none}
110
+ .gline{position:absolute;top:22px;bottom:0;width:2px;background:repeating-linear-gradient(180deg,var(--hi) 0 4px,transparent 4px 8px)}
111
+ .gline.now{background:var(--gold);width:3px}
112
+ @media (max-width:520px){#gantt{--lw:104px}}
113
+ /* ---- lobby: an old-school title screen + server browser */
114
+ body.lobby::after{content:"";position:fixed;inset:0;pointer-events:none;z-index:30;background:repeating-linear-gradient(rgba(0,0,0,.16) 0 2px,transparent 2px 4px)}
115
+ #lobby{max-width:980px;margin:0 auto;padding:8px 0 24px;text-align:center}
116
+ .logo{font-size:32px;line-height:1.25;margin:18px 0 6px;color:var(--gold);text-shadow:4px 4px 0 #b0410e,8px 8px 0 var(--edge)}
117
+ .logo span{color:var(--fg);font-size:24px;text-shadow:4px 4px 0 var(--hi),8px 8px 0 var(--edge)}
118
+ .march{position:relative;height:56px;overflow:hidden;margin:6px 0}
119
+ .walker{position:absolute;left:-64px;bottom:4px;animation:across 16s steps(160) infinite}
120
+ .blink{color:var(--gold);margin:6px 0 14px;animation:blink 1s steps(1) infinite}
121
+ .lbox{text-align:left;padding:12px 14px}.lbox h2{margin:12px 0 6px}.lbox h2:first-child{margin-top:0}
122
+ .lrow,.lhead{display:grid;grid-template-columns:14px minmax(0,1.7fr) 64px 72px 84px 64px 70px 74px;gap:8px;align-items:center;font-size:8px;padding:6px 6px}
123
+ .lhead{color:var(--mut);border-bottom:3px solid var(--edge)}
124
+ .lrow{width:100%;text-align:left;background:none;border:0;border-bottom:2px dashed var(--panel2);cursor:pointer}
125
+ .lrow:hover,.lrow.on{background:var(--panel2)}.lrow.on .cur{visibility:visible}.cur{visibility:hidden;color:var(--gold);animation:blink .6s steps(1) infinite}
126
+ .lrow:focus-visible{outline:3px solid var(--gold);outline-offset:-3px}
127
+ .lrow b{color:var(--fg);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lrow .sid{display:block;color:var(--mut);font-weight:400;margin-top:2px}
128
+ .lrow .n{display:inline-flex;align-items:center;gap:4px}.on-air{color:var(--ok)}.on-air::before{content:"";display:inline-block;width:8px;height:8px;background:var(--ok);margin-right:5px;animation:blink 1s steps(1) infinite}
129
+ .ltools{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin-bottom:10px;font-size:8px}
130
+ .ltools input,.ltools select{font:inherit;font-size:8px;color:var(--fg);background:var(--panel2);border:3px solid var(--edge);padding:6px 8px;min-width:0}
131
+ .ltools input{flex:1 1 160px}.ltools select{flex:0 1 220px;max-width:100%}
132
+ .ltools input:focus-visible,.ltools select:focus-visible{outline:3px solid var(--gold);outline-offset:2px}
133
+ .pager{display:flex;gap:10px;align-items:center;justify-content:center;margin:10px 0 2px;font-size:8px}
134
+ .btn:disabled{opacity:.4;cursor:default}
135
+ .empty{padding:14px 6px;color:var(--mut);font-size:8px;line-height:2}
136
+ #lparty{margin-top:14px;padding:12px 14px;text-align:left;min-height:96px}#lparty .row{gap:4px;margin:8px 0}
137
+ #lparty .hero{animation:idle 1.6s steps(1) infinite}#lparty .run{animation:walk .5s steps(1) infinite}
138
+ .hint{color:var(--mut);font-size:8px;margin-top:14px}
139
+ @media (max-width:760px){.lrow,.lhead{grid-template-columns:14px minmax(0,1fr) 56px 64px 70px}.lrow>:nth-child(n+6),.lhead>:nth-child(n+6){display:none}.logo{font-size:24px}.logo span{font-size:16px}}
140
+ @keyframes across{to{transform:translateX(calc(100vw + 128px))}}
141
+ @keyframes blink{50%{opacity:0}}
142
+ @keyframes idle{0%{transform:none}50%{transform:translateY(-2px)}}
143
+ @keyframes walk{0%{transform:none}50%{transform:translateY(-4px)}}
144
+ @keyframes march{to{background-position:9px 0}}
145
+ @keyframes poof{0%{transform:scale(0);opacity:0}60%{transform:scale(1.12);opacity:1}100%{transform:none}}
146
+ @keyframes rise{0%{transform:none;opacity:1}100%{transform:translateY(-28px);opacity:0}}
147
+ @keyframes slide{from{transform:translateX(12px);opacity:0}to{transform:none;opacity:1}}
148
+ @media (prefers-reduced-motion:reduce){*,*::before,*::after{animation:none!important;transition:none!important}}
149
+ </style></head><body>
150
+ <section id="lobby" hidden><h1 class="logo">AGENT<br><span>TIMELINES</span></h1><div class="march" id="march" aria-hidden="true"></div>
151
+ <p class="blink">SELECT A DUNGEON</p>
152
+ <div class="px lbox"><div class="ltools"><input id="lq" type="search" placeholder="SEARCH" aria-label="Search sessions" autocomplete="off"><select id="lproj" aria-label="Project"></select><span id="lcount" class="mut"></span><button class="btn" id="lclear" type="button" hidden>CLEAR</button></div>
153
+ <h2>LIVE DUNGEONS</h2><div class="lhead" aria-hidden="true"><span></span><span>DUNGEON</span><span>PARTY</span><span>QUESTING</span><span>COST</span><span>ISSUES</span><span>STATUS</span><span>SEEN</span></div>
154
+ <div id="lv"></div><h2>HALL OF RECORDS</h2><div id="hr"></div>
155
+ <div class="pager" id="lpager" hidden><button class="btn" id="lprev" type="button">&lt; PREV</button><span id="lpage" class="mut" aria-live="polite"></span><button class="btn" id="lnext" type="button">NEXT &gt;</button></div></div>
156
+ <div class="px" id="lparty" aria-live="polite"></div><p class="hint">UP/DOWN SELECT · ENTER JOIN · / SEARCH · LEFT/RIGHT PAGE · REFRESHES EVERY 3S</p></section>
157
+ <header id="hud" class="px"><h1><span id="h1">Loading…</span><span class="views" role="group" aria-label="View"><a class="btn" id="vlobby" href="index.html">&lt; LOBBY</a><button class="btn" id="vchart" type="button" aria-pressed="true">CHART</button><button class="btn" id="vmap" type="button" aria-pressed="false">MAP</button><button class="btn" id="vtime" type="button" aria-pressed="false">TIMELINE</button></span></h1>
158
+ <div class="stats" id="stats"></div><div id="meta" class="meta mut"></div><div id="xp" class="xp" title="Gold by model family"></div><div id="err" class="err" hidden></div>
159
+ <details><summary>How to read the dungeon</summary><dl id="legend"></dl></details>
160
+ <details><summary>Cost by model</summary><dl id="costs"></dl></details></header>
161
+ <div class="wrap" id="main"><div id="graph" class="px" tabindex="0" aria-label="Party tree: the guild master on the left, the heroes they sent out to the right"></div><div id="mapbox" class="px" hidden><div class="mapbar"><button class="btn" id="zout" type="button" aria-label="Zoom out">−</button><span id="zl" class="mut">2X</span><button class="btn" id="zin" type="button" aria-label="Zoom in">+</button><span class="mut">click a hero for details</span></div><div id="mapscroll"><canvas id="map" aria-label="Dungeon map: one room per agent; click a character for details"></canvas></div></div>
162
+ <div id="gantt" class="px" hidden><div class="mapbar"><button class="btn" id="gout" type="button" aria-label="Zoom out">−</button><span id="gzl" class="mut">1X</span><button class="btn" id="gin" type="button" aria-label="Zoom in">+</button><button class="btn" id="gfit" type="button">FIT</button><span class="mut">click a bar for details</span></div><div id="gscroll"><div id="ginner"><div class="gaxis" id="gaxis" aria-hidden="true"></div><div id="grows" role="group" aria-label="Agents on a shared time axis"></div><div id="gcols" aria-hidden="true"></div></div></div></div>
163
+ <aside id="side" class="px" aria-label="Quest log and party"><h2>QUEST LOG</h2><ol id="feed"></ol><details><summary>Whole party</summary><ol id="list"></ol></details></aside></div>
164
+ <div id="tip" class="px" role="tooltip" hidden></div><aside id="panel" class="px" role="dialog" aria-label="Hero details" hidden><button id="px" type="button" aria-label="Close">×</button><div id="pb"></div></aside>
165
+ <script src="dungeon.js"></script><script src="actors.js"></script><script src="lobby.js"></script><script src="gantt.js"></script>
166
+ <script>
167
+ (function(){"use strict";
168
+ var SID=(/[?&]sid=([\w-]+)/.exec(location.search)||[])[1],INDEX=!SID,DATA=INDEX?"index.data.js":SID+".data.js";
169
+ var FAMS=["fable","mythos","opus","sonnet","haiku","other"];
170
+ /* sprite cells [sheet, column, row]: td = Tiny Dungeon (12 columns), ob = 1-Bit Pack (48 columns) */
171
+ var HERO={main:["td",0,7],executor:["td",0,8],scanner:["td",1,7],plan:["td",4,8],writer:["td",3,8],runner:["td",4,7],other:["td",3,7],workflow:["td",5,7],wfrun:["td",8,7],ghost:["td",1,10]};
172
+ var ICON={heart:["ob",39,10],heart0:["ob",40,10],coin:["ob",43,12],skull:["ob",38,11],key:["ob",32,11],hourglass:["ob",39,12],trophy:["ob",40,16],stop:["ob",37,21],warn:["ob",35,21],potion:["ob",39,11],chest:["td",5,7]};
173
+ var TITLE={main:"GUILD MASTER",executor:"KNIGHT",scanner:"ROGUE",plan:"SAGE",writer:"BARD",runner:"BARBARIAN",other:"VIKING",workflow:"TREASURE RUN"};
174
+ var FLAGTXT={"no-model":"The spawn call set no model.","no-effort":"The spawn call set no effort.",
175
+ "model-mismatch":"A gateway or fallback ran a different model than requested.","effort-mismatch":"The effort that ran differs from the one requested.",
176
+ "guard-block":"A guard hook blocked the main session from executing.","handoff":"The agent wrote a HANDOFF and stopped.",
177
+ "tool-limit":"A hook told this agent it hit the tool-call limit.","ignored-limit":"The agent got the tool-call limit message, wrote no handoff and kept going.",
178
+ "handoff-not-continued":"The agent handed off but no later agent continues it.","auto-filled":"A spawn hook filled a missing model or effort.",
179
+ "orphan":"No parent spawn call was found for this agent.","frontmatter":"Model and effort come from the agent's own file.",
180
+ "ctx":"One turn's context (input + cache read + cache write) reached the context limit."};
181
+ var cur=null,by={},live=true,sel=null,idx=null,prev={},seenEv={},first=true,PR={},RP={},prBusy=false;
182
+ function $(id){return document.getElementById(id)}
183
+ function el(tag,cls,txt){var e=document.createElement(tag);if(cls)e.className=cls;if(txt!=null)e.textContent=txt;return e}
184
+ function spr(s,scale,cls){var e=el("i","spr "+s[0]+(cls?" "+cls:""));e.style.setProperty("--c",s[1]);e.style.setProperty("--r",s[2]);e.style.setProperty("--s",scale||2);e.setAttribute("aria-hidden","true");return e}
185
+ function info(f){return f==="handoff"||f==="tool-limit"||f==="auto-filled"||f==="frontmatter"||f.slice(0,4)==="ctx-"}
186
+ function pad(n){return n<10?"0"+n:""+n}
187
+ function hms(t){var d=new Date(t*1000);return pad(d.getHours())+":"+pad(d.getMinutes())}
188
+ function fdur(s){s=Math.max(0,Math.round(s));return s>=3600?Math.floor(s/3600)+"h"+pad(Math.floor(s%3600/60))+"m":s>=60?Math.floor(s/60)+"m"+pad(s%60)+"s":s+"s"}
189
+ function ftok(n){return n>=1e6?(n/1e6).toFixed(1)+"M":n>=1e3?(n/1e3).toFixed(1)+"k":String(n)}
190
+ function tokTxt(t){return t?"input "+t.in.toLocaleString()+" · cache write "+t.cc.toLocaleString()+" · cache read "+t.cr.toLocaleString()+" · output "+t.out.toLocaleString():""}
191
+ function usd(x){return x==null?"–":x===0?"$0.00":x<0.01?"<$0.01":x<100?"$"+x.toFixed(2):"$"+Math.round(x).toLocaleString()}
192
+ function tname(s){var m=/^mcp__(.+?)__(.+)$/.exec(s);return m?m[1]+":"+m[2]:s}
193
+ function plural(n,w){return n+" "+w+(n===1?"":"s")}
194
+ function lsGet(k){try{return localStorage.getItem(k||"tlTab")}catch(e){return null}}
195
+ function lsSet(v,k){try{localStorage.setItem(k||"tlTab",v)}catch(e){}}
196
+ function ssGet(k){try{return sessionStorage.getItem(k)||""}catch(e){return ""}} /* the lobby filter lives for this tab only */
197
+ function ssSet(k,v){try{sessionStorage.setItem(k,v)}catch(e){}}
198
+ function dur(d,n){var e=n.running?(d.status==="live"?Math.max(d.now,Date.now()/1000):d.now):n.end;return n.start==null||e==null?null:e-n.start}
199
+ function issues(n){return n.flags.filter(function(f){return !info(f)})}
200
+ function famOf(n){return n.id==="main"?"plan":n.fam}
201
+ function cls(n){return n.id==="main"?"main":n.kind==="wf"?"workflow":HERO[n.tier]?n.tier:"other"}
202
+ function state(d,n){
203
+ if(n.id==="main")return n.running?["run","QUESTING"]:d.status==="live"?["wait","RESTING"]:["ok","HOME"];
204
+ if(n.running)return ["run","QUESTING"];
205
+ if(n.kind==="wf"&&n.wf.state==="failed")return ["bad","DEFEATED"];
206
+ if(n.limit&&n.handoff)return ["stop","RETREATED"];
207
+ if(n.flags.indexOf("ignored-limit")>=0)return ["bad","BERSERK"];
208
+ return ["ok","VICTORY"]}
209
+ function sprite(n,st){return st[0]==="bad"&&n.kind!=="wf"?HERO.ghost:n.kind==="wf"&&n.running?HERO.wfrun:HERO[cls(n)]}
210
+ function name(n){return n.id==="main"?"GUILD MASTER":"#"+n.n+" "+(TITLE[cls(n)]||n.type.toUpperCase())}
211
+
212
+ /* tool budget as hearts: 5 hearts = the tool-call limit, one lost per fifth used */
213
+ function hearts(d,n){var h=el("span","hearts");h.title=n.tools+" of "+d.toolLimit+" tool calls";
214
+ var left=Math.max(0,Math.min(5,Math.ceil(5*(1-n.tools/d.toolLimit))));for(var i=0;i<5;i++)h.append(spr(i<left?ICON.heart:ICON.heart0,1));return h}
215
+ /* "?" badge: hover or focus shows what a game term really means */
216
+ function q(game,real){var b=el("button","q","?");b.type="button";b.dataset.g=game;b.dataset.r=real;b.setAttribute("aria-label",game+" means "+real);return b}
217
+ var REAL={main:"the main Claude Code session (planner)",executor:"an executor agent (general-purpose or another sonnet agent)",scanner:"an Explore agent (read-only search)",
218
+ plan:"a Plan agent",writer:"a writer agent",runner:"a runner agent",other:"an agent of another type",workflow:"a Workflow run (a group of agents run by a script)"};
219
+ var STREAL={QUESTING:"running now",RESTING:"session is live, main session is waiting for you",HOME:"session ended",VICTORY:"finished",
220
+ RETREATED:"stopped at the tool-call limit and handed off",BERSERK:"got the tool-limit message, wrote no handoff and kept going",DEFEATED:"failed: killed, or no result recorded"};
221
+ function chip(fl,c,icon,txt,tip){var k=el("span","chip "+c);if(icon)k.append(spr(icon,1));k.append(txt);k.title=tip;fl.append(k)}
222
+ function card(d,n){
223
+ var st=state(d,n),c=el("div","card px"),bad=issues(n),fl=el("div","fl"),t=dur(d,n),lim=!!d.toolLimit&&n.id!=="main"&&!n.exempt,p=prev[n.id];
224
+ c.dataset.id=n.id;c.dataset.st=st[0];c.dataset.fam=famOf(n);c.tabIndex=0;c.setAttribute("role","button");c.title=n.desc||"";
225
+ c.setAttribute("aria-label",name(n)+", "+st[1]+", "+(n.desc||"")+(bad.length?", "+plural(bad.length,"issue"):""));
226
+ if(bad.length)c.dataset.bad="1";if(sel===n.id)c.classList.add("sel");
227
+ if(!first&&!p)c.classList.add("spawn");else if(p&&p.run&&!n.running&&st[0]==="ok")c.classList.add("win");
228
+ var top=el("div","c1"),who=el("div","who");who.append(el("b",null,name(n)),el("span","tag",st[1]),q(name(n)+" · "+st[1],(n.id==="main"?"":"agent "+n.n+": ")+REAL[cls(n)]+" ("+n.type+") · "+STREAL[st[1]]));
229
+ top.append(spr(sprite(n,st),3,"hero"),who);
230
+ var r1=el("div","row"),dot=el("span","dot");dot.dataset.fam=famOf(n);r1.append(dot,el("code",null,n.ran));
231
+ var r2=el("div","row");
232
+ if(lim)r2.append(hearts(d,n));else r2.append(spr(ICON.potion,1),el("span","nums",n.tools+" tools"));
233
+ if(lim&&n.tools>=d.toolLimit*0.8)r2.firstChild.title+=n.tools>=d.toolLimit?" (over the limit)":" (near the limit)";
234
+ var gold=el("span","nums",n.tokens?ftok(n.tokens):"–");gold.title="Gold = input-equivalent tokens. "+tokTxt(n.tok);
235
+ var r3=el("div","row"),cost=el("span","nums",usd(n.usd)+(n.unpriced?"+":"")+" API price");cost.title="$ = API list price for these tokens"+(n.unpriced?" (some models have no price set, so the real figure is higher)":"");
236
+ r2.append(spr(ICON.hourglass,1),el("span","nums",t==null?"–":fdur(t)),spr(ICON.coin,1),gold,
237
+ q(lim?"hearts · hourglass · gold · $":"potion · hourglass · gold · $",(lim?"hearts = tool calls left before the "+d.toolLimit+"-call limit ("+n.tools+" used)":n.tools+" tool calls (no limit applies)")+" · hourglass = run time · gold = input-equivalent tokens · $ = API list price for these tokens"));
238
+ bad.forEach(function(f){if(f!=="handoff-not-continued")chip(fl,"flag",ICON.skull,f==="guard-block"?plural(d.denials,"guard block"):f,FLAGTXT[f]||f)});
239
+ if(n.cont&&by[n.cont])chip(fl,"key",ICON.key,"FROM #"+by[n.cont].n,"Picked up the key: continues the work of #"+by[n.cont].n);
240
+ if(n.handoff){var m=d.agents.filter(function(a){return a.cont===n.id})[0];
241
+ if(m)chip(fl,"key",ICON.key,"TO #"+m.n,"Handed off; #"+m.n+" continues it");else chip(fl,"flag",ICON.key,"KEY DROPPED",FLAGTXT["handoff-not-continued"])}
242
+ n.flags.forEach(function(f){if(info(f)&&f!=="handoff")chip(fl,"",f==="tool-limit"?ICON.stop:null,f==="tool-limit"?"LIMIT "+n.limit:f.slice(0,4)==="ctx-"?"CTX "+ftok(n.peak):f,FLAGTXT[f]||FLAGTXT.ctx)});
243
+ r3.append(cost);c.append(top,el("div","desc",n.desc||"(no description)"),r1,r2,r3);
244
+ var tb=n.toolsBy||[];
245
+ if(tb.length){var r4=el("div","row nums"),all=tb.map(function(x){return tname(x[0])+" "+x[1]}),rest=tb.length-3;
246
+ r4.textContent=all.slice(0,3).join(" · ")+(rest>0?" +"+rest:"");r4.title=all.join(" · ");c.append(r4)}
247
+ if(n.reply){var rp=el("div","desc reply","» "+n.reply);c.append(rp)}
248
+ c.append(fl);
249
+ return c}
250
+ function branch(d,n,kids,seen){
251
+ seen[n.id]=1;var b=el("div","br");if(n.running)b.classList.add("run");b.append(card(d,n));
252
+ var ks=(kids[n.id]||[]).filter(function(k){return !seen[k.id]});
253
+ if(ks.length){var k=el("div","kids");ks.forEach(function(x){k.append(branch(d,x,kids,seen))});b.append(k)}
254
+ return b}
255
+ function stat(icon,val,lab,tip,c){var s=el("span","stat"+(c?" "+c:""));s.append(spr(icon,2),el("b",null,val),el("span","mut",lab),q(lab,tip));return s}
256
+
257
+ function render(d){
258
+ if(cur&&cur.id!==d.id){prev={};first=true;seenEv={}}
259
+ cur=d;live=d.status==="live";
260
+ var ags=d.agents,run=ags.filter(function(a){return a.running&&a.kind!=="wf"}).length,kids={};
261
+ var P={id:"main",n:0,type:"planner",desc:"Main session: plans and sends out the party",ran:d.planner,fam:d.pfam,running:run>0||d.prun,start:d.t0,end:d.t1,tools:d.tools,
262
+ tokens:d.tokens,tok:d.tok,usd:d.usd,unpriced:d.unpriced,toolsBy:d.toolsBy,reply:d.reply,replyLen:d.replyLen,peak:d.peak,flags:d.denials?["guard-block"]:[],exempt:true,req:"-",tier:"main",prompt:"",parent:null};
263
+ by={main:P};ags.forEach(function(a){by[a.id]=a});
264
+ ags.forEach(function(a){var p=by[a.parent]?a.parent:"main";(kids[p]=kids[p]||[]).push(a)});
265
+ var fid=document.activeElement&&document.activeElement.dataset?document.activeElement.dataset.id:null,g=$("graph");
266
+ g.replaceChildren(branch(d,P,kids,{}));
267
+ if(fid){var f=g.querySelector('[data-id="'+CSS.escape(fid)+'"]');if(f)f.focus()}
268
+ prev={};[P].concat(ags).forEach(function(n){prev[n.id]={run:n.running}});
269
+ var wfs=ags.filter(function(a){return a.kind==="wf"}).length,iss=ags.reduce(function(k,a){return k+issues(a).length},0)+d.denials;
270
+ var tot=FAMS.reduce(function(k,f){return k+(d.models[f]||0)},0);
271
+ $("h1").textContent=live?"DUNGEON RUN IN PROGRESS":"RUN COMPLETE";
272
+ $("stats").replaceChildren(stat(HERO.executor,String(ags.length-wfs),"heroes","subagents spawned"),stat(ICON.hourglass,String(run),"questing","agents running now"),
273
+ stat(ICON.trophy,String(ags.length-wfs-run),"back","agents finished"),stat(ICON.coin,ftok(tot),"gold","tokens, weighted by cost (input 1, cache write 1.25, cache read 0.1, output 5)"),
274
+ stat(ICON.coin,usd(d.cost&&d.cost.total),"cost","What these tokens would cost at Anthropic API list prices as of "+(d.cost?d.cost.asof:"?")+". A subscription plan is not billed per token. Unpriced models: "+(d.cost&&d.cost.unpriced.length?d.cost.unpriced.join(", "):"none")+"."),
275
+ stat(ICON.skull,String(iss),"issues","red flags: rule breaks such as no-model, model-mismatch, orphan, guard blocks",iss?"bad":null));
276
+ if(wfs)$("stats").append(stat(ICON.chest,String(wfs),"treasure runs","Workflow runs"));
277
+ var a=el("a",null,"SESSION "+d.id.slice(0,8));a.href="index.html?sid="+encodeURIComponent(d.id);a.title=d.id;
278
+ $("meta").replaceChildren((live?"LIVE "+hms(d.now):"ENDED")+" · ",a," · "+(d.cwd||"cwd unknown")+" · guild master "+d.planner);
279
+ $("xp").replaceChildren.apply($("xp"),FAMS.filter(function(f){return d.models[f]}).map(function(f){var s=el("span");s.dataset.fam=f;s.style.width=(d.models[f]*100/tot)+"%";s.title=f+" "+Math.round(d.models[f]*100/tot)+"%: "+tokTxt(d.modelTok[f])+(d.cost?" · "+usd(famUsd(d,f)):"");return s}));
280
+ costs(d);
281
+ $("err").hidden=!d.errors.length;$("err").textContent=d.errors.length+" parse problems: "+d.errors.join("; ");
282
+ var EV={spawn:HERO.executor,done:ICON.trophy,wf:ICON.chest,handoff:ICON.key,cont:ICON.key,guard:ICON.skull,limit:ICON.stop,refused:ICON.warn};
283
+ $("feed").replaceChildren.apply($("feed"),d.events.slice(-20).reverse().map(function(ev){
284
+ var k=ev.t+ev.k+ev.text,li=el("li"),b=el(ev.aid&&by[ev.aid]?"button":"span");if(!first&&!seenEv[k])li.className="new";seenEv[k]=1;
285
+ b.append(spr(EV[ev.k]||ICON.warn,1),el("time",null,hms(ev.t)),el("span",null,ev.text));
286
+ if(b.tagName==="BUTTON"){b.type="button";b.addEventListener("click",function(){openPanel(ev.aid)})}
287
+ li.append(b);return li}));
288
+ $("list").replaceChildren.apply($("list"),[P].concat(ags).map(function(n){var li=el("li"),b=el("button"),st=state(d,n);b.type="button";
289
+ b.append(spr(sprite(n,st),1),el("span",null,name(n)+" · "+st[1]+" · "+(n.desc||"").slice(0,40)));b.addEventListener("click",function(){openPanel(n.id)});li.append(b);return li}));
290
+ if(VIEW==="time")drawGantt(d);if(sel)fill();first=false;if(VIEW==="map"&&!M.raf)M.raf=requestAnimationFrame(mapFrame)}
291
+
292
+ function famUsd(d,f){return d.cost.models.reduce(function(k,m){return k+(m.fam===f?m.usd:0)},0)}
293
+ function costs(d){var dl=$("costs"),c=d.cost;dl.replaceChildren();
294
+ if(!c){dl.append(el("dd",null,"No cost data in this file. Run: agent-timelines rebuild"));return}
295
+ c.models.forEach(function(m){var un=c.unpriced.indexOf(m.model)>=0,t=el("dt",null,m.model.replace(/^claude-/,""));t.style.color="var(--"+(FAMS.indexOf(m.fam)>=0?m.fam:"other")+")";
296
+ dl.append(t,el("dd",null,(un?"no price set":usd(m.usd))+" · "+tokTxt(m.tok)))})}
297
+ function kv(a,b){var r=el("div","kv");r.append(el("b",null,a),el("span",null,b));return r}
298
+ function openPanel(id){sel=id;
299
+ var n=by[id];if(n&&((n.brief&&!n.prompt&&PR[id]==null)||(n.replyLen&&n.replyLen!==(RP[id]||"").length)))loadPrompts(); /* a reply that grew since the last load is fetched again; shortcut: replyLen counts code points, so an astral-plane reply refetches on each open */
300
+ $("panel").hidden=false;render(cur);var c=cardOf(id);if(c)c.scrollIntoView({block:"nearest",inline:"nearest"});$("px").focus()}
301
+ function closePanel(){if(sel===null)return;var id=sel;sel=null;$("panel").hidden=true;render(cur);var c=cardOf(id);if(c)c.focus()}
302
+ function cardOf(id){return (VIEW==="time"?$("grows"):$("graph")).querySelector('[data-id="'+CSS.escape(id)+'"]')} /* the card or bar row in the view that is showing */
303
+ function fill(){
304
+ var n=by[sel],pb=$("pb"),d=cur;if(!n){sel=null;$("panel").hidden=true;return}
305
+ var st=state(d,n),t=dur(d,n),h=el("h3",null,name(n));h.style.cssText="margin-top:0;padding-right:32px";h.prepend(spr(sprite(n,st),3,"hero"),document.createTextNode(" "));
306
+ pb.replaceChildren(h,el("p",null,n.desc||"(no description)"));
307
+ pb.append(kv("Status",st[1]+(n.kind==="wf"?" ("+n.wf.status+")":"")));
308
+ if(n.n&&n.kind!=="wf")pb.append(kv("Model/effort","asked "+n.req+" → ran "+n.ran));else pb.append(kv("Ran",n.ran));
309
+ pb.append(kv("Time",t==null?"–":(n.running?"questing ":"")+fdur(t)),kv("Tools",n.tools+(d.toolLimit&&n.n&&!n.exempt?" of "+d.toolLimit:"")),kv("Gold",n.tokens?n.tokens.toLocaleString()+" input-equiv tokens":"–"),kv("Cost",usd(n.usd)+(n.unpriced?" (some models have no price set)":"")));
310
+ if(n.tok)pb.append(kv("Split",tokTxt(n.tok)),kv("Peak context",n.peak?n.peak.toLocaleString()+(n.peak>=d.ctxLimit?" (over "+d.ctxLimit.toLocaleString()+")":""):"–"));
311
+ if(n.n)pb.append(kv("Class",n.tier+" ("+n.type+")"),kv(n.kind==="wf"?"Run id":"Agent id",n.id),kv("Sent by",by[n.parent]?name(by[n.parent]):"GUILD MASTER"));
312
+ if(n.phase)pb.append(kv("Phase",n.phase));
313
+ if(n.brief)pb.append(kv("Brief",n.brief.toLocaleString()+" chars"));
314
+ if(n.wf){var w=n.wf;pb.append(kv("Workflow",w.name),kv("Agents",w.agents+(Object.keys(w.states).length?" ("+Object.keys(w.states).map(function(k){return w.states[k]+" "+k}).join(", ")+")":"")));
315
+ if(w.phases.length)pb.append(kv("Phases",w.phases.join(" → ")));
316
+ if(w.error)pb.append(el("h3",null,"ERROR"),el("pre",null,w.error));
317
+ if(w.logs.length)pb.append(el("h3",null,"LOG"),el("pre",null,w.logs.join("\n")))}
318
+ if(n.handoff){var m=d.agents.filter(function(a){return a.cont===n.id})[0];pb.append(kv("Handed off",n.handoff+(m?" → #"+m.n+" continues":" (key dropped)")))}
319
+ if(n.limit)pb.append(kv("Tool limit","warned at "+n.limit+", ended at "+n.tools));
320
+ if(n.cont)pb.append(kv("Continues",by[n.cont]?name(by[n.cont]):n.cont));
321
+ if(n.id==="main"&&d.denials)pb.append(kv("Guard blocks",String(d.denials)));
322
+ if(n.toolsBy&&n.toolsBy.length){pb.append(el("h3",null,"TOOLS"));n.toolsBy.forEach(function(x){pb.append(kv(tname(x[0]),String(x[1])))})}
323
+ if(n.flags.length){pb.append(el("h3",null,"FLAGS"));n.flags.forEach(function(f){var r=el("div","kv"),k=el("b",info(f)?null:"bad",f);r.append(k,el("span",null,FLAGTXT[f]||FLAGTXT.ctx));pb.append(r)})}
324
+ var pr=n.prompt||PR[n.id];
325
+ if(pr)pb.append(el("h3",null,"ORDERS (FULL PROMPT)"),el("pre",null,pr));
326
+ else if(n.brief)pb.append(el("h3",null,"ORDERS (FULL PROMPT)"),el("p","mut","Loading…"));
327
+ var rp=RP[n.id]||n.reply;
328
+ if(rp){pb.append(el("h3",null,n.running?"SAYING":"REPORT (FINAL REPLY)"),el("pre",null,rp));if(n.replyLen>rp.length)pb.append(el("p","mut","Loading…"))}}
329
+
330
+ function legend(){var dl=$("legend");
331
+ [[HERO.main,"Guild master = the main session. Heroes by class: knight executor, rogue scanner, sage planner, bard writer, barbarian runner, viking other."],
332
+ [HERO.workflow,"Treasure run = a Workflow run (a mimic while it runs)."],[HERO.ghost,"Ghost = berserk: ignored the tool limit."],
333
+ [ICON.heart,"Hearts (only when a tool limit is configured) = tool budget: 5 hearts is the tool-call limit; one is lost per fifth used."],[ICON.coin,"Gold = input-equivalent tokens (input 1, cache write 1.25, cache read 0.1, output 5). $ = API list price of the same tokens."],
334
+ [ICON.hourglass,"Timeline view: one bar per agent on a shared clock, rows in party-tree order; a striped bar is still running, a key or skull after a bar is a handoff or an issue. Zoom with + and -."],
335
+ [ICON.key,"Key = a handoff: TO #6 on the one that stopped, FROM #4 on the one that continues. KEY DROPPED = nobody continued."],
336
+ [ICON.skull,"Skull chips are issues; grey chips are info. Name colour = model family that ran; 'x?/y?' = requested, not seen running yet."]]
337
+ .forEach(function(x){var t=el("dt");t.append(spr(x[0],2));dl.append(t,el("dd",null,x[1]))})}
338
+
339
+ /* ---- lobby: live sessions and the hall of records as a game-server browser; ENTER or click joins */
340
+ var lsel=lsGet();
341
+ function lobbySpr(p){return p[3]&&p[1]!=="wf"?HERO.ghost:p[1]==="wf"?(p[2]?HERO.wfrun:HERO.workflow):HERO[p[0]]||HERO.other}
342
+ function ago(t){var s=Date.now()/1000-t;return s<90?"NOW":s<3600?Math.floor(s/60)+"M AGO":s<86400?Math.floor(s/3600)+"H AGO":Math.floor(s/86400)+"D AGO"}
343
+ function dname(r){return (String(r.cwd||"?").split("/").pop()||"/").toUpperCase()}
344
+ function join(id){location.href="index.html?sid="+encodeURIComponent(id)}
345
+ function lrow(r){var b=el("button","lrow"+(r.id===lsel?" on":""));b.type="button";b.dataset.id=r.id;if(r.id===lsel)b.setAttribute("aria-current","true");
346
+ var nm=el("b",null,dname(r));nm.append(el("span","sid",r.id.slice(0,8)+(r.planner?" · "+r.planner:"")));
347
+ function n(icon,v,c,tip){var x=el("span","n"+(c?" "+c:""));x.append(spr(icon,1),String(v));if(tip)x.title=tip;return x}
348
+ b.append(el("span","cur",">"),nm,n(HERO.executor,r.heroes),n(ICON.hourglass,r.running),n(ICON.coin,usd(r.usd),null,"Gold "+ftok(r.gold||0)),n(ICON.skull,r.issues,r.issues?"bad":null),
349
+ el("span",r.live?"on-air":"mut",r.live?"LIVE":"ENDED"),el("span","mut",ago(r.updated)));
350
+ b.setAttribute("aria-label",dname(r)+", "+(r.live?"live":"ended")+", "+plural(r.heroes,"agent")+", "+r.running+" working, "+plural(r.issues,"issue")+". Press Enter to join.");
351
+ b.addEventListener("mouseenter",function(){pickRow(r.id)});b.addEventListener("focus",function(){pickRow(r.id)});b.addEventListener("click",function(){join(r.id)});return b}
352
+ function empty(t){return el("div","empty",t)}
353
+ function pickRow(id){if(lsel===id)return;lsel=id;lsSet(id);[].forEach.call(document.querySelectorAll(".lrow"),function(b){var on=b.dataset.id===id;b.classList.toggle("on",on);
354
+ if(on)b.setAttribute("aria-current","true");else b.removeAttribute("aria-current")});drawParty()}
355
+ function drawParty(){var r=(idx.sessions||[]).filter(function(x){return x.id===lsel})[0],box=$("lparty");
356
+ if(!r){box.replaceChildren(el("h2",null,"PARTY"),empty("Pick a dungeon to see its party."));return}
357
+ var row=el("div","row");row.append(spr(HERO.main,3,"hero"+(r.live&&r.running?" run":"")));
358
+ r.party.forEach(function(p,i){var e=spr(lobbySpr(p),3,"hero"+(p[2]?" run":""));e.style.animationDelay=(i%4)*0.2+"s";row.append(e)});
359
+ if(r.heroes+r.workflows>r.party.length)row.append(el("span","mut","+"+(r.heroes+r.workflows-r.party.length)));
360
+ box.replaceChildren(el("h2",null,"PARTY · "+dname(r)),row,el("div","mut",(r.last?"LAST: "+r.last:"No events yet")));}
361
+ var LQ=ssGet("tlQ"),LPROJ=ssGet("tlProj"),LPAGE=0,PAGE=15,LPK=null;
362
+ function drawLobby(){
363
+ var rows=idx.sessions||[],ps=Lobby.projects(rows),pk=JSON.stringify(ps),f=document.activeElement&&document.activeElement.classList.contains("lrow");
364
+ if(LPROJ&&!ps.some(function(p){return p.cwd===LPROJ}))LPROJ="";
365
+ if(pk!==LPK){LPK=pk;var sl=$("lproj"),o=el("option","","ALL PROJECTS");o.value=""; /* rebuilt only when the project list changed, so an open dropdown survives the poll */
366
+ sl.replaceChildren.apply(sl,[o].concat(ps.map(function(p){var x=el("option","",p.name.toUpperCase()+" ("+p.n+")");x.value=p.cwd;return x})))}
367
+ $("lproj").value=LPROJ;
368
+ var vis=Lobby.filter(rows,LQ,LPROJ),lv=vis.filter(function(r){return r.live}),pg=Lobby.page(vis.filter(function(r){return !r.live}),LPAGE,PAGE),act=!!(LQ||LPROJ);
369
+ LPAGE=pg.page;
370
+ if(!lv.concat(pg.items).some(function(r){return r.id===lsel}))lsel=(lv[0]||pg.items[0]||{}).id;
371
+ $("lv").replaceChildren.apply($("lv"),lv.length?lv.map(lrow):[empty(act?"NO MATCH.":"NO ADVENTURERS ONLINE. Start a Claude Code session: its party appears here on the first hook event.")]);
372
+ $("hr").replaceChildren.apply($("hr"),pg.items.length?pg.items.map(lrow):[empty(act?"NO MATCH.":"NO RECORDS YET.")]);
373
+ $("lcount").textContent=act?vis.length+" OF "+rows.length:rows.length+(rows.length===1?" SESSION":" SESSIONS");$("lclear").hidden=!act;
374
+ $("lpager").hidden=pg.pages<2;$("lpage").textContent="PAGE "+(pg.page+1)+" OF "+pg.pages;$("lprev").disabled=pg.page<1;$("lnext").disabled=pg.page>=pg.pages-1;
375
+ if(f){var b=document.querySelector('.lrow[data-id="'+CSS.escape(lsel)+'"]');if(b)b.focus()}
376
+ drawParty();live=true}
377
+ function lobbyFilter(){LPAGE=0;ssSet("tlQ",LQ);ssSet("tlProj",LPROJ);if(idx)drawLobby()}
378
+ function lobbyKeys(e){var t=e.target.tagName;if(!INDEX||t==="INPUT"||t==="SELECT")return;
379
+ if(e.key==="/"&&!e.ctrlKey&&!e.metaKey&&!e.altKey){e.preventDefault();$("lq").focus();return}
380
+ if(e.key==="ArrowLeft"||e.key==="PageUp"||e.key==="ArrowRight"||e.key==="PageDown"){e.preventDefault();LPAGE+=(e.key==="ArrowLeft"||e.key==="PageUp")?-1:1;if(idx)drawLobby();return}
381
+ var bs=[].slice.call(document.querySelectorAll(".lrow")),i=bs.findIndex(function(b){return b.dataset.id===lsel});
382
+ if(e.key==="ArrowDown"||e.key==="ArrowUp"){e.preventDefault();var n=bs[Math.max(0,Math.min(bs.length-1,i+(e.key==="ArrowDown"?1:-1)))];if(n){pickRow(n.dataset.id);n.focus()}}
383
+ else if(e.key==="Enter"&&i>=0&&!(document.activeElement&&document.activeElement.classList.contains("lrow"))){e.preventDefault();join(lsel)}}
384
+ function march(){var m=$("march"),cast=["main","executor","scanner","plan","writer","runner","other"];
385
+ cast.forEach(function(k,i){var w=el("span","walker");w.style.animationDelay=(i*0.9)+"s";w.append(spr(HERO[k],3,"hero run"));m.append(w)});
386
+ var foe=el("span","walker");foe.style.animationDelay=(cast.length*0.9+0.6)+"s";foe.append(spr(["td",0,9],3,"hero run"));m.append(foe)}
387
+
388
+ window.TL=function(d){if(!d||typeof d!=="object")return;if(INDEX){idx=d;drawLobby();return}
389
+ if(cur&&cur.id===d.id&&cur.now===d.now&&!d.prun&&!d.agents.some(function(a){return a.running}))return; /* same build, nothing ticking */
390
+ render(d)};
391
+ window.TLP=function(p){if(p&&cur&&p.id===cur.id&&p.prompts){PR=p.prompts;RP=p.replies||{};if(sel)fill()}};
392
+ function loadPrompts(){if(prBusy||INDEX)return;prBusy=true;var sc=document.createElement("script");sc.src=SID+".prompts.js?t="+Date.now();
393
+ sc.onload=sc.onerror=function(){prBusy=false;sc.remove()};document.head.appendChild(sc)}
394
+ function poll(){var sc=document.createElement("script");sc.src=DATA+"?t="+Date.now();sc.onload=sc.onerror=function(){sc.remove()};document.head.appendChild(sc)}
395
+ setInterval(function(){if(live&&document.visibilityState==="visible")poll()},3000);
396
+ $("graph").addEventListener("click",function(e){var c=e.target.closest(".card");if(c)openPanel(c.dataset.id)});
397
+ $("graph").addEventListener("keydown",function(e){var c=e.target.closest(".card");if(c&&(e.key==="Enter"||e.key===" ")){e.preventDefault();openPanel(c.dataset.id)}});
398
+ $("px").addEventListener("click",closePanel);
399
+ document.addEventListener("keydown",function(e){if(e.key==="Escape")closePanel()});
400
+ function showTip(e){var b=e.target.closest&&e.target.closest(".q"),t=$("tip");if(!b){t.hidden=true;return}
401
+ t.replaceChildren(el("b",null,b.dataset.g),document.createTextNode(" = "+b.dataset.r));t.hidden=false;
402
+ var r=b.getBoundingClientRect(),w=t.offsetWidth,h=t.offsetHeight,x=Math.min(Math.max(8,r.left+r.width/2-w/2),innerWidth-w-8),y=r.top-h-8<8?r.bottom+8:r.top-h-8;
403
+ t.style.left=x+"px";t.style.top=y+"px"}
404
+ document.addEventListener("mouseover",showTip);document.addEventListener("focusin",showTip);
405
+ document.addEventListener("mouseout",function(e){if(e.target.closest&&e.target.closest(".q"))$("tip").hidden=true});
406
+ document.addEventListener("focusout",function(){$("tip").hidden=true});
407
+ document.addEventListener("click",function(e){if(e.target.closest&&e.target.closest(".q"))e.stopPropagation()},true); /* a ? inside a card shows help, not the panel */
408
+ /* ---- map view: the same session as a dungeon (dungeon.js lays out rooms, actors.js draws the characters) */
409
+ var VIEW=["map","time"].indexOf(lsGet("tlView"))>=0?lsGet("tlView"):"chart",GZ=[1,2,4,8],gz=0,RMQ=matchMedia("(prefers-reduced-motion: reduce)").matches,SHEETS={td:new Image(),ob:new Image()},M={scale:2,mem:{},shape:"",sid:null,raf:0};
410
+ SHEETS.td.src="tiny-dungeon.png";SHEETS.ob.src="one-bit.png";
411
+ function mapNodes(d){var run=d.agents.some(function(a){return a.running&&a.kind!=="wf"});
412
+ return [{id:"main",parent:null,running:run||d.prun,flags:[]}].concat(d.agents.map(function(a){return {id:a.id,parent:a.parent,tier:a.tier,kind:a.kind,running:a.running,flags:a.flags,wf:a.wf}}))}
413
+ function setView(v){VIEW=v;lsSet(v,"tlView");$("graph").hidden=v!=="chart";$("mapbox").hidden=v!=="map";$("gantt").hidden=v!=="time";
414
+ $("vchart").setAttribute("aria-pressed",String(v==="chart"));$("vmap").setAttribute("aria-pressed",String(v==="map"));$("vtime").setAttribute("aria-pressed",String(v==="time"));
415
+ if(v==="map"&&!M.raf)M.raf=requestAnimationFrame(mapFrame);if(v==="time"&&cur)drawGantt(cur)}
416
+ /* ---- timeline view: Gantt.layout (gantt.js) places the bars; rows are buttons in party-tree order */
417
+ function drawGantt(d){
418
+ var now=live?Math.max(d.now,Date.now()/1000):d.now,ns=[by.main].concat(d.agents).map(function(n){return {id:n.id,parent:n.parent,start:n.start,end:n.running?now:n.end,running:n.running}});
419
+ var L=Gantt.layout(ns,null,live?now:null),fid=document.activeElement&&document.activeElement.closest&&document.activeElement.closest("#grows")?document.activeElement.dataset.id:null,pc=function(x){return (x*100).toFixed(3)+"%"};
420
+ $("ginner").style.width=GZ[gz]*100+"%";
421
+ $("gaxis").replaceChildren.apply($("gaxis"),[el("div","glabel"),(function(){var t=el("div","gtrack");L.ticks.forEach(function(k){var x=el("span","gtick",k.label);x.style.left=pc(k.x);t.append(x)});return t})()]);
422
+ var cols=[];L.ticks.forEach(function(k){var x=el("i","gline");x.style.left=pc(k.x);cols.push(x)});
423
+ if(live&&now>=L.t0&&now<=L.t1){var nw=el("i","gline now");nw.style.left=pc((now-L.t0)/(L.t1-L.t0));nw.title="now";cols.push(nw)}
424
+ $("gcols").replaceChildren.apply($("gcols"),cols);
425
+ $("grows").replaceChildren.apply($("grows"),L.rows.map(function(r){
426
+ var n=by[r.id],st=state(d,n),bad=issues(n),t=dur(d,n),b=el("button","grow"+(sel===n.id?" sel":"")),lb=el("span","glabel"),tr=el("span","gtrack"),bar=el("i","gbar"+(n.running?" run":""));
427
+ b.type="button";b.dataset.id=n.id;b.dataset.fam=famOf(n);b.dataset.st=st[0];
428
+ b.title=(n.desc||name(n))+" · "+(t==null?"–":fdur(t))+" · "+usd(n.usd)+(n.unpriced?"+":"")+(n.start==null?"":" · from "+hms(n.start));
429
+ b.setAttribute("aria-label",name(n)+", "+st[1]+", "+(n.desc||"")+(bad.length?", "+plural(bad.length,"issue"):"")+", "+(t==null?"no duration":fdur(t)));
430
+ lb.style.setProperty("--d",r.depth);lb.append(spr(sprite(n,st),1),el("span",null,name(n)));
431
+ bar.style.left=pc(r.x);bar.style.width=pc(r.w);
432
+ if(n.handoff)bar.append(spr(ICON.key,1));if(bad.length)bar.append(spr(ICON.skull,1));
433
+ tr.append(bar);b.append(lb,tr);return b}));
434
+ if(fid){var f=$("grows").querySelector('[data-id="'+CSS.escape(fid)+'"]');if(f)f.focus()}}
435
+ function gzoom(dz){var sc=$("gscroll"),w=$("ginner").offsetWidth||1,c=(sc.scrollLeft+sc.clientWidth/2)/w,n=Math.max(0,Math.min(GZ.length-1,gz+dz));if(n===gz)return;gz=n;$("gzl").textContent=GZ[gz]+"X";
436
+ if(cur)drawGantt(cur);sc.scrollLeft=c*$("ginner").offsetWidth-sc.clientWidth/2}
437
+ function mapFrame(now){
438
+ if(VIEW!=="map"||!cur){M.raf=0;return}
439
+ M.raf=requestAnimationFrame(mapFrame);
440
+ if(!SHEETS.td.complete||!SHEETS.ob.complete||!window.Dungeon||!window.Actors)return;
441
+ var nodes=mapNodes(cur),sc=$("mapscroll"),cv=$("map"),s=M.scale,key=nodes.map(function(n){return n.id+(n.running?"*":"")}).join();
442
+ if(M.sid!==cur.id){M.sid=cur.id;M.d=null;M.mem={};M.shape=""}
443
+ if(key!==M.shape||M.bs!==s){
444
+ if(!M.d||M.d.rooms.length!==nodes.length||M.bs!==s){
445
+ var old=M.d,oldS=M.bs,d=Dungeon.generate(cur.id,nodes),c=M.center;
446
+ M.bg=document.createElement("canvas");M.bg.width=d.w*16*s;M.bg.height=d.h*16*s;Dungeon.render(M.bg.getContext("2d"),d,SHEETS,s);
447
+ cv.width=M.bg.width;cv.height=M.bg.height;cv.style.width=cv.width+"px";cv.style.height=cv.height+"px";
448
+ if(c){sc.scrollLeft=c[0]*cv.width-sc.clientWidth/2;sc.scrollTop=c[1]*cv.height-sc.clientHeight/2;M.center=null} /* zoom keeps the centre */
449
+ else if(old&&oldS===s){sc.scrollLeft+=(old.ox-d.ox)*16*s;sc.scrollTop+=(old.oy-d.oy)*16*s} /* the map grew up/left: keep the view still */
450
+ else{var h=d.rooms[0];sc.scrollLeft=(h.cx-d.ox)*16*s-sc.clientWidth/2;sc.scrollTop=(h.cy-d.oy)*16*s-sc.clientHeight/2} /* first look: the guild hall */
451
+ M.d=d;M.bs=s}
452
+ M.acts=Actors.place(cur.id,M.d,nodes);M.shape=key}
453
+ Actors.stamp(M.acts,M.mem,now);
454
+ var ctx=cv.getContext("2d");ctx.imageSmoothingEnabled=false;ctx.drawImage(M.bg,0,0);Actors.draw(ctx,M.d,M.acts,SHEETS,s,now,RMQ);
455
+ if(sel){var a=M.acts.filter(function(x){return x.id===sel})[0];if(a){var w=a.walk&&a.walk.at||[a.x,a.y]; /* gold frame on the selected hero */
456
+ ctx.strokeStyle="#ffd23f";ctx.lineWidth=s;ctx.strokeRect((w[0]-M.d.ox)*16*s-s,(w[1]-M.d.oy)*16*s-s,16*s+2*s,16*s+2*s)}}}
457
+ function zoom(dz){var sc=$("mapscroll"),cv=$("map"),n=Math.max(1,Math.min(6,M.scale+dz));if(n===M.scale)return;
458
+ M.center=[(sc.scrollLeft+sc.clientWidth/2)/(cv.width||1),(sc.scrollTop+sc.clientHeight/2)/(cv.height||1)];M.scale=n;$("zl").textContent=n+"X"}
459
+ $("map").addEventListener("click",function(e){if(!M.d)return;var r=this.getBoundingClientRect(),S=16*M.scale,x=Math.floor((e.clientX-r.left)/S)+M.d.ox,y=Math.floor((e.clientY-r.top)/S)+M.d.oy;
460
+ var hit=M.acts.filter(function(a){var w=a.walk&&a.walk.at||[a.x,a.y];return (w[0]===x&&w[1]===y)||(a.fx===x&&a.fy===y)||(a.x===x&&a.y===y)})[0];if(hit)openPanel(hit.id)});
461
+ $("vchart").addEventListener("click",function(){setView("chart")});$("vmap").addEventListener("click",function(){setView("map")});$("vtime").addEventListener("click",function(){setView("time")});
462
+ $("gin").addEventListener("click",function(){gzoom(1)});$("gout").addEventListener("click",function(){gzoom(-1)});$("gfit").addEventListener("click",function(){gzoom(-gz)});
463
+ $("grows").addEventListener("click",function(e){var r=e.target.closest(".grow");if(r)openPanel(r.dataset.id)});
464
+ $("zin").addEventListener("click",function(){zoom(1)});$("zout").addEventListener("click",function(){zoom(-1)});
465
+ setView(VIEW);
466
+ if(INDEX){$("lq").value=LQ;
467
+ $("lq").addEventListener("input",function(){LQ=this.value;lobbyFilter()});
468
+ $("lq").addEventListener("keydown",function(e){if(e.key==="Escape"&&this.value){this.value="";LQ="";lobbyFilter()}else if(e.key==="Escape"||e.key==="Enter")this.blur()});
469
+ $("lproj").addEventListener("change",function(){LPROJ=this.value;lobbyFilter()});
470
+ $("lclear").addEventListener("click",function(){LQ=LPROJ="";$("lq").value="";lobbyFilter()});
471
+ $("lprev").addEventListener("click",function(){LPAGE--;drawLobby()});$("lnext").addEventListener("click",function(){LPAGE++;drawLobby()});
472
+ document.body.classList.add("lobby");$("lobby").hidden=false;$("hud").hidden=true;$("main").hidden=true;march();document.addEventListener("keydown",lobbyKeys)}
473
+ legend();poll()})();
474
+ </script></body></html>