@henryz2004/agency 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 (41) hide show
  1. package/README.md +106 -0
  2. package/lib/codex.js +211 -0
  3. package/lib/control.js +168 -0
  4. package/lib/live.js +493 -0
  5. package/lib/opencode.js +447 -0
  6. package/lib/paths.js +12 -0
  7. package/lib/roster.js +204 -0
  8. package/lib/transcript.js +361 -0
  9. package/lib/usage.js +346 -0
  10. package/package.json +27 -0
  11. package/public/app.js +1021 -0
  12. package/public/audio-controls.js +165 -0
  13. package/public/avatar.js +467 -0
  14. package/public/characters/dev-auburn.json +32 -0
  15. package/public/characters/dev-auburn.png +0 -0
  16. package/public/characters/dev-beanie.json +32 -0
  17. package/public/characters/dev-beanie.png +0 -0
  18. package/public/characters/dev-glasses.json +32 -0
  19. package/public/characters/dev-glasses.png +0 -0
  20. package/public/chat-panel.css +514 -0
  21. package/public/chat-panel.js +815 -0
  22. package/public/index.html +190 -0
  23. package/public/lab.html +129 -0
  24. package/public/leaderboard.js +222 -0
  25. package/public/metric.js +34 -0
  26. package/public/mock-agents.js +70 -0
  27. package/public/mock.js +277 -0
  28. package/public/music/Console_Morning.mp3 +0 -0
  29. package/public/music/Midnight_Desk.mp3 +0 -0
  30. package/public/music/The_Plant_Beside_the_Door.mp3 +0 -0
  31. package/public/music/Three_AM_Window.mp3 +0 -0
  32. package/public/office.js +1484 -0
  33. package/public/sound.js +382 -0
  34. package/public/sprites.js +983 -0
  35. package/public/style.css +506 -0
  36. package/public/ui.js +50 -0
  37. package/scripts/_pixpng.mjs +104 -0
  38. package/scripts/animsheet.mjs +60 -0
  39. package/scripts/charsheet.mjs +61 -0
  40. package/scripts/install-hook.mjs +120 -0
  41. package/server.js +370 -0
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "dev-beanie",
3
+ "image": "dev-beanie.png",
4
+ "cellW": 27,
5
+ "cellH": 34,
6
+ "cols": 4,
7
+ "anchorX": 13,
8
+ "anchorY": 33,
9
+ "anims": {
10
+ "idle": [
11
+ 0,
12
+ 1
13
+ ],
14
+ "type": [
15
+ 4,
16
+ 5,
17
+ 6,
18
+ 7
19
+ ],
20
+ "walk": [
21
+ 8,
22
+ 9,
23
+ 10,
24
+ 11
25
+ ]
26
+ },
27
+ "fps": {
28
+ "idle": 2,
29
+ "type": 6,
30
+ "walk": 8
31
+ }
32
+ }
Binary file
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "dev-glasses",
3
+ "image": "dev-glasses.png",
4
+ "cellW": 27,
5
+ "cellH": 34,
6
+ "cols": 4,
7
+ "anchorX": 13,
8
+ "anchorY": 33,
9
+ "anims": {
10
+ "idle": [
11
+ 0,
12
+ 1
13
+ ],
14
+ "type": [
15
+ 4,
16
+ 5,
17
+ 6,
18
+ 7
19
+ ],
20
+ "walk": [
21
+ 8,
22
+ 9,
23
+ 10,
24
+ 11
25
+ ]
26
+ },
27
+ "fps": {
28
+ "idle": 2,
29
+ "type": 6,
30
+ "walk": 8
31
+ }
32
+ }
@@ -0,0 +1,514 @@
1
+ /* chat-panel.css — the read-only "open agent's chat" peek panel. Kept in its
2
+ own file (linked from index.html) so it composes with style.css without
3
+ touching it. Reuses the shared :root design tokens defined there — including
4
+ the --t-* / --p-* type scale, so the panel matches the rest of the app. */
5
+
6
+ /* A compact card that FLOATS beside the selected agent (office.js sets left/top
7
+ each frame so it tracks the agent + camera) — not a screen-blocking rail. */
8
+ .chat-panel {
9
+ position: fixed;
10
+ left: 0; top: 80px; /* office.js overrides left/top to pin it beside the agent */
11
+ width: 380px;
12
+ max-width: 92vw;
13
+ max-height: 62vh;
14
+ z-index: 200;
15
+ display: flex;
16
+ flex-direction: column;
17
+ background: linear-gradient(180deg, var(--card), var(--card2));
18
+ border: 1px solid var(--line);
19
+ border-radius: 10px;
20
+ box-shadow: 0 14px 44px rgba(0, 0, 0, 0.55);
21
+ overflow: hidden; /* the body scrolls instead of the whole card */
22
+ font-family: var(--term);
23
+ /* show/hide is a quick fade+pop (position comes from left/top, not transform) */
24
+ opacity: 0;
25
+ transform: translateY(6px) scale(0.98);
26
+ pointer-events: none;
27
+ transition: opacity 0.16s ease, transform 0.16s ease;
28
+ }
29
+ .chat-panel.open {
30
+ opacity: 1;
31
+ transform: translateY(0) scale(1);
32
+ pointer-events: auto;
33
+ }
34
+
35
+ /* head — a vertical stack: the title/close row, then the compact rename+hide
36
+ strip. (The rename + hide controls were consolidated up here so the body is
37
+ pure status/transcript.) */
38
+ .cp-head {
39
+ display: flex;
40
+ flex-direction: column;
41
+ gap: 8px;
42
+ padding: 11px 13px;
43
+ border-bottom: 1px solid var(--line);
44
+ background: linear-gradient(180deg, #121828, #0c111b);
45
+ }
46
+ .cp-headtop {
47
+ display: flex;
48
+ align-items: flex-start;
49
+ gap: 9px;
50
+ }
51
+ .cp-id {
52
+ min-width: 0;
53
+ flex: 1 1 auto;
54
+ }
55
+ .cp-title {
56
+ font-size: 17px;
57
+ color: var(--gold);
58
+ line-height: 1.1;
59
+ display: flex;
60
+ align-items: center;
61
+ gap: 7px;
62
+ }
63
+ .cp-role {
64
+ font-family: var(--pixel);
65
+ font-size: 7px;
66
+ line-height: 1;
67
+ letter-spacing: 1px;
68
+ border-radius: 2px;
69
+ padding: 2px 4px;
70
+ }
71
+ .cp-role.lead {
72
+ color: #2a1f05;
73
+ background: var(--gold);
74
+ }
75
+ .cp-role.teammate {
76
+ color: #06231f;
77
+ background: var(--cyan);
78
+ }
79
+ .cp-sub {
80
+ font-size: var(--t-fine);
81
+ color: var(--muted);
82
+ margin-top: 3px;
83
+ overflow: hidden;
84
+ text-overflow: ellipsis;
85
+ white-space: nowrap;
86
+ }
87
+ .cp-sub .cp-dept {
88
+ color: var(--cyan);
89
+ }
90
+ .cp-close {
91
+ flex: 0 0 auto;
92
+ background: transparent;
93
+ border: 1px solid var(--line);
94
+ color: var(--muted);
95
+ font-size: var(--t-sub);
96
+ line-height: 1;
97
+ border-radius: 4px;
98
+ padding: 4px 7px;
99
+ cursor: pointer;
100
+ }
101
+ /* empty edit strip (no sessionId agent) collapses so it adds no header gap */
102
+ .cp-edit:empty {
103
+ display: none;
104
+ }
105
+ .cp-close:hover {
106
+ color: var(--ink);
107
+ border-color: var(--line2);
108
+ background: rgba(255, 255, 255, 0.04);
109
+ }
110
+
111
+ /* body */
112
+ .cp-body {
113
+ flex: 1 1 auto;
114
+ overflow-y: auto;
115
+ padding: 11px 13px;
116
+ display: flex;
117
+ flex-direction: column;
118
+ gap: 9px;
119
+ }
120
+
121
+ /* ---- status + metrics card ----------------------------------------------- */
122
+ /* The panel's primary content for any real Claude session (idle OR working): a
123
+ truthful status badge from the agent's own state, the task it's on, a 30-min
124
+ activity readout, and — only when a tool is in flight — a live "doing now"
125
+ line. No conversation dump; just honest status. */
126
+ .cp-activity {
127
+ border: 1px solid var(--line);
128
+ border-left: 3px solid var(--green);
129
+ border-radius: 6px;
130
+ background: linear-gradient(180deg, #0f1a16, #0c1512);
131
+ padding: 9px 11px;
132
+ display: flex;
133
+ flex-direction: column;
134
+ gap: 6px;
135
+ }
136
+ .cp-activity.shell { border-left-color: #ffb454; background: linear-gradient(180deg, #1a160c, #15110a); }
137
+ .cp-activity.idle { border-left-color: var(--muted2); background: var(--card2); }
138
+ .cp-activity.done { border-left-color: var(--cyan); background: linear-gradient(180deg, #0c1620, #0b131c); }
139
+
140
+ .cp-act-head { display: flex; align-items: center; gap: 8px; }
141
+ .cp-act-dot {
142
+ width: 9px; height: 9px; border-radius: 50%; flex: 0 0 auto;
143
+ background: var(--green); box-shadow: 0 0 7px var(--green);
144
+ animation: cp-pulse 1.2s ease-in-out infinite;
145
+ }
146
+ .cp-act-dot.shell { background: #ffb454; box-shadow: 0 0 7px #ffb454; }
147
+ .cp-act-dot.idle { background: var(--muted2); box-shadow: none; animation: none; }
148
+ .cp-act-dot.done { background: var(--cyan); box-shadow: 0 0 7px var(--cyan); animation: none; }
149
+ .cp-act-status {
150
+ font-family: var(--pixel); font-size: 7px; letter-spacing: 1px;
151
+ text-transform: uppercase; color: var(--green);
152
+ }
153
+ .cp-activity.shell .cp-act-status { color: #ffb454; }
154
+ .cp-activity.idle .cp-act-status { color: var(--muted); }
155
+ .cp-activity.done .cp-act-status { color: var(--cyan); }
156
+
157
+ /* the task/topic the agent is working toward */
158
+ .cp-act-task {
159
+ font-size: var(--t-body); line-height: 1.3; color: var(--ink);
160
+ overflow-wrap: anywhere;
161
+ }
162
+ /* the headline 30-min activity readout: "⚡ 14 tool calls · 12.3k tokens" */
163
+ .cp-act-metrics {
164
+ font-family: var(--mono); font-size: var(--t-sub); line-height: 1.3; color: var(--ink);
165
+ overflow-wrap: anywhere;
166
+ }
167
+ .cp-act-bolt { color: var(--gold); margin-right: 3px; }
168
+ .cp-act-win { color: var(--muted2); }
169
+ .cp-activity.idle .cp-act-metrics { color: var(--muted); }
170
+
171
+ /* the live action line: "doing now Editing app.js". Empty (collapsed) when
172
+ no tool is in flight, so an idle agent shows nothing here. */
173
+ .cp-act-now {
174
+ font-size: var(--t-sub); line-height: 1.35; color: var(--muted);
175
+ overflow-wrap: anywhere;
176
+ }
177
+ .cp-act-now.cp-act-empty { display: none; }
178
+ .cp-act-label {
179
+ font-family: var(--pixel); font-size: 7px; letter-spacing: 1px;
180
+ text-transform: uppercase; color: var(--muted2); margin-right: 5px;
181
+ }
182
+ .cp-act-target { color: var(--cyan); }
183
+ .cp-dim { color: var(--muted2); } /* generic dim (blocked-banner hint, etc.) */
184
+ .cp-act-now .cp-dim { color: var(--muted2); }
185
+ .cp-act-sub { font-size: var(--t-fine); color: #46d6cf; }
186
+
187
+ /* notes + teammate launch brief */
188
+ .cp-note {
189
+ font-size: var(--t-sub);
190
+ color: var(--muted);
191
+ line-height: 1.4;
192
+ }
193
+ .cp-note .cp-dim {
194
+ color: var(--muted2);
195
+ }
196
+ .cp-brief {
197
+ font-size: var(--t-sub);
198
+ line-height: 1.4;
199
+ color: var(--ink);
200
+ white-space: pre-wrap;
201
+ overflow-wrap: anywhere;
202
+ border: 1px solid var(--line2);
203
+ border-left: 3px solid var(--cyan);
204
+ border-radius: 6px;
205
+ padding: 8px 10px;
206
+ background: var(--card2);
207
+ }
208
+
209
+ /* last-message peek — the most recent turn(s) so a desk maps to a real session
210
+ at a glance (the rest of the panel is honest-status, not a transcript). */
211
+ .cp-lastmsg {
212
+ margin-top: 2px; padding: 9px 11px;
213
+ background: var(--card2); border: 1px solid var(--line2);
214
+ border-radius: 8px; display: flex; flex-direction: column; gap: 8px;
215
+ }
216
+ .cp-lastmsg-head {
217
+ font-family: var(--pixel); font-size: 7px; letter-spacing: 1px;
218
+ text-transform: uppercase; color: var(--muted2);
219
+ }
220
+ .cp-msg { display: flex; gap: 8px; align-items: flex-start; }
221
+ .cp-msg-who {
222
+ flex: 0 0 auto; margin-top: 2px; line-height: 1;
223
+ font-family: var(--pixel); font-size: 6px; letter-spacing: 1px;
224
+ text-transform: uppercase; padding: 3px 4px; border-radius: 3px;
225
+ }
226
+ .cp-msg-you .cp-msg-who { color: #04222e; background: var(--cyan); }
227
+ .cp-msg-agent .cp-msg-who { color: #06231f; background: var(--green); }
228
+ .cp-msg-text { font-size: var(--t-sub); line-height: 1.4; color: var(--ink); overflow-wrap: anywhere; }
229
+ .cp-msg-agent .cp-msg-text { color: var(--muted); }
230
+
231
+ /* footer actions: one clean row — a short note, then "Open in Terminal" + copy.
232
+ The raw resume command is no longer shown (too small to read); copy still
233
+ copies the full command to the clipboard. */
234
+ .cp-actions {
235
+ margin-top: 2px;
236
+ display: flex;
237
+ flex-direction: column;
238
+ gap: 8px;
239
+ border-top: 1px dotted var(--line2);
240
+ padding-top: 9px;
241
+ }
242
+ .cp-action-row {
243
+ display: flex;
244
+ align-items: center;
245
+ gap: 6px;
246
+ }
247
+ .cp-action-note {
248
+ flex: 1 1 auto;
249
+ min-width: 0;
250
+ font-size: var(--t-sub);
251
+ line-height: 1.3;
252
+ color: var(--muted);
253
+ overflow-wrap: anywhere;
254
+ }
255
+ .cp-copy {
256
+ flex: 0 0 auto;
257
+ font-family: var(--pixel);
258
+ font-size: 7px;
259
+ letter-spacing: 1px;
260
+ text-transform: uppercase;
261
+ color: var(--ink);
262
+ background: #16203a;
263
+ border: 1px solid var(--line);
264
+ border-radius: 4px;
265
+ padding: 6px 8px;
266
+ cursor: pointer;
267
+ }
268
+ .cp-copy:hover {
269
+ border-color: var(--cyan);
270
+ color: var(--cyan);
271
+ }
272
+ .cp-copy.ok {
273
+ color: var(--green);
274
+ border-color: var(--green);
275
+ }
276
+
277
+ /* primary action: open a terminal + resume the session */
278
+ .cp-open {
279
+ flex: 0 0 auto;
280
+ font-family: var(--pixel);
281
+ font-size: 7px;
282
+ letter-spacing: 1px;
283
+ text-transform: uppercase;
284
+ color: #2a1f05;
285
+ background: var(--gold);
286
+ border: 1px solid var(--gold);
287
+ border-radius: 4px;
288
+ padding: 6px 8px;
289
+ cursor: pointer;
290
+ }
291
+ .cp-open:hover {
292
+ filter: brightness(1.1);
293
+ }
294
+ .cp-open:disabled {
295
+ opacity: 0.6;
296
+ cursor: default;
297
+ }
298
+
299
+ /* customize controls (rename + hide): a compact single-row strip in the HEADER
300
+ (a slim rename field + Save, then a small hide toggle). Quiet, dense; reuses
301
+ the shared :root tokens. */
302
+ .cp-cust-row {
303
+ display: flex;
304
+ align-items: stretch;
305
+ gap: 6px;
306
+ }
307
+ .cp-cust-input {
308
+ flex: 1 1 auto;
309
+ min-width: 0;
310
+ box-sizing: border-box;
311
+ font-family: var(--term);
312
+ font-size: var(--t-sub);
313
+ color: var(--ink);
314
+ background: #0c1119;
315
+ border: 1px solid var(--line2);
316
+ border-radius: 4px;
317
+ padding: 4px 7px;
318
+ }
319
+ .cp-cust-input:focus {
320
+ outline: none;
321
+ border-color: var(--gold);
322
+ }
323
+ .cp-cust-save {
324
+ flex: 0 0 auto;
325
+ font-family: var(--pixel);
326
+ font-size: 7px;
327
+ letter-spacing: 1px;
328
+ text-transform: uppercase;
329
+ color: var(--ink);
330
+ background: #16203a;
331
+ border: 1px solid var(--line);
332
+ border-radius: 4px;
333
+ padding: 0 9px;
334
+ cursor: pointer;
335
+ }
336
+ .cp-cust-save:hover {
337
+ border-color: var(--cyan);
338
+ color: var(--cyan);
339
+ }
340
+ .cp-cust-save.ok {
341
+ color: var(--green);
342
+ border-color: var(--green);
343
+ }
344
+ .cp-cust-save:disabled {
345
+ opacity: 0.6;
346
+ cursor: default;
347
+ }
348
+ /* hide: a small icon toggle (👁 / 🙈) — quiet, square-ish, sits beside Save */
349
+ .cp-cust-hide {
350
+ flex: 0 0 auto;
351
+ font-size: 12px;
352
+ line-height: 1;
353
+ color: var(--muted);
354
+ background: transparent;
355
+ border: 1px solid var(--line2);
356
+ border-radius: 4px;
357
+ padding: 0 8px;
358
+ cursor: pointer;
359
+ }
360
+ .cp-cust-hide:hover {
361
+ color: var(--ink);
362
+ border-color: var(--line);
363
+ background: rgba(255, 255, 255, 0.04);
364
+ }
365
+ .cp-cust-hide.ok {
366
+ color: var(--green);
367
+ border-color: var(--green);
368
+ }
369
+ .cp-cust-hide:disabled {
370
+ opacity: 0.6;
371
+ cursor: default;
372
+ }
373
+
374
+ /* "needs you" HUD pill (Control Phase-1): a non-intrusive topbar chip counting
375
+ agents paused on a Stop hook. Lives here (not style.css) to keep the control
376
+ feature self-contained; reuses the shared :root tokens. */
377
+ .waiting-pill {
378
+ font-family: var(--pixel);
379
+ font-size: var(--p-chip);
380
+ letter-spacing: 1px;
381
+ color: #2a1f05;
382
+ background: var(--gold);
383
+ border: 1px solid var(--gold);
384
+ border-radius: 999px;
385
+ padding: 6px 11px;
386
+ cursor: pointer;
387
+ line-height: 1;
388
+ white-space: nowrap;
389
+ animation: cp-pulse 1.6s ease-in-out infinite;
390
+ }
391
+ .waiting-pill:hover {
392
+ filter: brightness(1.1);
393
+ }
394
+ .waiting-pill.hidden {
395
+ display: none;
396
+ }
397
+
398
+ /* reply box (Control Phase-1): shown above the transcript when an agent is
399
+ paused on a Stop hook and awaiting a reply. */
400
+ .cp-reply {
401
+ border: 1px solid var(--gold);
402
+ border-radius: 6px;
403
+ background: linear-gradient(180deg, #1a160b, #14110a);
404
+ padding: 10px 11px;
405
+ display: flex;
406
+ flex-direction: column;
407
+ gap: 8px;
408
+ }
409
+ /* the blocked banner reuses .cp-reply but drops the loud amber outline + fill —
410
+ the gold dot + heading already read as "blocked"; keep the box quiet. */
411
+ .cp-blocked {
412
+ border-color: var(--line);
413
+ background: var(--card2);
414
+ }
415
+ .cp-reply-head {
416
+ font-size: var(--t-fine);
417
+ color: var(--gold);
418
+ display: flex;
419
+ align-items: center;
420
+ gap: 7px;
421
+ line-height: 1.3;
422
+ }
423
+ .cp-reply-dot {
424
+ flex: 0 0 auto;
425
+ width: 8px;
426
+ height: 8px;
427
+ border-radius: 50%;
428
+ background: var(--gold);
429
+ box-shadow: 0 0 6px var(--gold);
430
+ animation: cp-pulse 1.4s ease-in-out infinite;
431
+ }
432
+ @keyframes cp-pulse {
433
+ 0%, 100% { opacity: 1; }
434
+ 50% { opacity: 0.35; }
435
+ }
436
+ .cp-reply-q {
437
+ font-size: var(--t-sub);
438
+ line-height: 1.35;
439
+ color: var(--muted);
440
+ white-space: pre-wrap;
441
+ overflow-wrap: anywhere;
442
+ border-left: 3px solid var(--line2);
443
+ padding-left: 8px;
444
+ max-height: 6.5em;
445
+ overflow-y: auto;
446
+ }
447
+ .cp-reply-input {
448
+ width: 100%;
449
+ box-sizing: border-box;
450
+ resize: vertical;
451
+ font-family: var(--term);
452
+ font-size: var(--t-sub);
453
+ line-height: 1.35;
454
+ color: var(--ink);
455
+ background: #0c1119;
456
+ border: 1px solid var(--line2);
457
+ border-radius: 4px;
458
+ padding: 7px 9px;
459
+ }
460
+ .cp-reply-input:focus {
461
+ outline: none;
462
+ border-color: var(--gold);
463
+ }
464
+ .cp-reply-input:disabled {
465
+ opacity: 0.6;
466
+ }
467
+ .cp-reply-row {
468
+ display: flex;
469
+ align-items: center;
470
+ justify-content: space-between;
471
+ gap: 8px;
472
+ }
473
+ .cp-reply-status {
474
+ font-size: var(--t-fine);
475
+ color: var(--muted2);
476
+ min-width: 0;
477
+ overflow: hidden;
478
+ text-overflow: ellipsis;
479
+ white-space: nowrap;
480
+ }
481
+ .cp-reply-status.err {
482
+ color: var(--red, #e05d5d);
483
+ }
484
+ .cp-reply-send {
485
+ flex: 0 0 auto;
486
+ font-family: var(--pixel);
487
+ font-size: var(--p-chip);
488
+ letter-spacing: 1px;
489
+ text-transform: uppercase;
490
+ color: #2a1f05;
491
+ background: var(--gold);
492
+ border: 1px solid var(--gold);
493
+ border-radius: 4px;
494
+ padding: 7px 11px;
495
+ cursor: pointer;
496
+ }
497
+ .cp-reply-send:hover {
498
+ filter: brightness(1.1);
499
+ }
500
+ .cp-reply-send:disabled {
501
+ opacity: 0.6;
502
+ cursor: default;
503
+ }
504
+ .cp-reply-sent {
505
+ font-size: var(--t-body);
506
+ color: var(--green);
507
+ padding: 2px 0;
508
+ }
509
+
510
+ @media (max-width: 1000px) {
511
+ .chat-panel {
512
+ width: 92vw;
513
+ }
514
+ }