codedeck 0.4.0 → 0.4.2

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 (47) hide show
  1. package/dist/cli/commands/done.js +35 -0
  2. package/dist/cli/commands/done.js.map +1 -0
  3. package/dist/cli/commands/send.js +2 -0
  4. package/dist/cli/commands/send.js.map +1 -1
  5. package/dist/cli/commands/web.js +89 -6
  6. package/dist/cli/commands/web.js.map +1 -1
  7. package/dist/cli/index.js +2 -0
  8. package/dist/cli/index.js.map +1 -1
  9. package/dist/core/events.js.map +1 -1
  10. package/dist/core/roles.js +28 -5
  11. package/dist/core/roles.js.map +1 -1
  12. package/dist/core/session.js.map +1 -1
  13. package/dist/daemon/daemon.js +289 -93
  14. package/dist/daemon/daemon.js.map +1 -1
  15. package/dist/drivers/antigravity/driver.js +52 -38
  16. package/dist/drivers/antigravity/driver.js.map +1 -1
  17. package/dist/open/contract.js +6 -2
  18. package/dist/open/contract.js.map +1 -1
  19. package/dist/open/launchers/claude.js +8 -2
  20. package/dist/open/launchers/claude.js.map +1 -1
  21. package/dist/plugin/agents/auditor.md +13 -5
  22. package/dist/plugin/agents/general.md +355 -15
  23. package/dist/plugin/agents/orchestrator-edit.md +16 -5
  24. package/dist/plugin/agents/orchestrator-read.md +16 -5
  25. package/dist/plugin/agents/orchestrator.md +16 -5
  26. package/dist/plugin/agents/reviewer.md +6 -2
  27. package/dist/plugin/prompts/_partials/commit.md +145 -0
  28. package/dist/plugin/prompts/_partials/core.md +9 -0
  29. package/dist/plugin/prompts/_partials/dispatch.md +5 -0
  30. package/dist/plugin/prompts/_partials/pr-writer.md +186 -0
  31. package/dist/plugin/prompts/_partials/proof.md +2 -0
  32. package/dist/plugin/prompts/_partials/rename-run.md +1 -0
  33. package/dist/plugin/prompts/_partials/review-self.md +4 -0
  34. package/dist/plugin/prompts/_partials/worktree.md +2 -0
  35. package/dist/plugin/prompts/roles/auditor.md +35 -0
  36. package/dist/plugin/prompts/roles/general.md +29 -0
  37. package/dist/plugin/prompts/roles/orchestrator-edit.md +68 -0
  38. package/dist/plugin/prompts/roles/orchestrator-read.md +68 -0
  39. package/dist/plugin/prompts/roles/orchestrator.md +90 -0
  40. package/dist/plugin/prompts/roles/reviewer.md +37 -0
  41. package/dist/store/database.js +4 -0
  42. package/dist/store/database.js.map +1 -1
  43. package/dist/store/sessions.js +8 -3
  44. package/dist/store/sessions.js.map +1 -1
  45. package/dist/web/canvas-page.js +208 -57
  46. package/dist/web/canvas-page.js.map +1 -1
  47. package/package.json +1 -1
@@ -50,7 +50,7 @@ export const CANVAS_PAGE = `<!doctype html>
50
50
  #feed li.fresh { color: #f5f5f7; }
51
51
  @keyframes feedIn { from { opacity: 0; transform: translateY(-5px); } to { opacity: 1; } }
52
52
  #hint { left: 50%; transform: translateX(-50%); bottom: 16px; font-size: 12px; color: #98989f; white-space: nowrap; }
53
- #detail { position: fixed; top: 0; right: 0; bottom: 0; width: min(360px, 92vw); z-index: 20;
53
+ #detail { position: fixed; top: 0; right: 0; bottom: 0; width: min(400px, 94vw); z-index: 20;
54
54
  background: rgba(10, 12, 16, 0.92); border-left: 1px solid rgba(255,255,255,0.1);
55
55
  backdrop-filter: blur(14px); padding: 18px; overflow-y: auto; display: none; }
56
56
  #detail.open { display: block; }
@@ -69,6 +69,28 @@ export const CANVAS_PAGE = `<!doctype html>
69
69
  border: 1px solid rgba(255,255,255,0.12); background: transparent; color: #98989f;
70
70
  cursor: pointer; font-size: 14px; line-height: 1; font-family: inherit; }
71
71
  #detailClose:hover { color: #f5f5f7; border-color: rgba(255,255,255,0.25); }
72
+ #dChat { border: 1px solid rgba(255,255,255,0.08); border-radius: 10px; padding: 10px; max-height: 52vh; min-height: 140px; overflow-y: auto; display: flex; flex-direction: column; gap: 9px; }
73
+ /* Scroll fino escuro no painel e no chat (Firefox + WebKit): o padrao
74
+ claro do SO quebra o tema escuro da pagina. */
75
+ #detail, #dChat { scrollbar-width: thin; scrollbar-color: rgba(255,255,255,0.24) transparent; }
76
+ #detail::-webkit-scrollbar, #dChat::-webkit-scrollbar { width: 10px; }
77
+ #detail::-webkit-scrollbar-track, #dChat::-webkit-scrollbar-track { background: transparent; }
78
+ #detail::-webkit-scrollbar-thumb, #dChat::-webkit-scrollbar-thumb { background-color: rgba(255,255,255,0.18); border-radius: 8px; border: 3px solid transparent; background-clip: content-box; }
79
+ #detail::-webkit-scrollbar-thumb:hover, #dChat::-webkit-scrollbar-thumb:hover { background-color: rgba(255,255,255,0.32); }
80
+ #dChat .msg { font-size: 12.5px; line-height: 1.45; white-space: pre-wrap; word-break: break-word; padding: 7px 9px; border-radius: 9px; }
81
+ #dChat .msg .who { display: block; font-size: 10.5px; letter-spacing: 0.04em; color: #98989f; margin-bottom: 2px; }
82
+ #dChat .msg.user { background: rgba(10,132,255,0.16); }
83
+ #dChat .msg.assistant { background: rgba(255,255,255,0.05); }
84
+ #dChat .evt { font-size: 11.5px; color: #636366; }
85
+ #dChat .empty { color: #636366; font-size: 12px; }
86
+ #dSend { display: flex; gap: 8px; margin-top: 10px; }
87
+ #dSendText { flex: 1; min-width: 0; background: rgba(255,255,255,0.06); border: 1px solid rgba(255,255,255,0.12); border-radius: 10px; color: #f5f5f7; padding: 8px 10px; font-size: 12.5px; font-family: inherit; }
88
+ #dSendText:focus { outline: none; border-color: rgba(255,255,255,0.3); }
89
+ #dSendText:disabled { opacity: 0.4; }
90
+ #dSendBtn, #dArchive { border: 1px solid rgba(255,255,255,0.14); background: rgba(10,132,255,0.25); color: #f5f5f7; border-radius: 10px; padding: 8px 12px; cursor: pointer; font-family: inherit; font-size: 12.5px; }
91
+ #dSendBtn:disabled, #dArchive:disabled { opacity: 0.4; cursor: default; }
92
+ #dArchive { width: 100%; margin-top: 8px; }
93
+ #dSendMsg { font-size: 11.5px; color: #98989f; margin: 6px 0 0; min-height: 15px; }
72
94
  .node { position: absolute; width: 196px; background: #13161c; border: 1px solid rgba(255,255,255,.09);
73
95
  border-radius: 14px; padding: 11px 13px; pointer-events: auto; cursor: grab; user-select: none;
74
96
  box-shadow: 0 8px 28px rgba(0,0,0,.45); }
@@ -105,8 +127,14 @@ export const CANVAS_PAGE = `<!doctype html>
105
127
  <h2 id="dTitle"></h2>
106
128
  <p class="dsub" id="dSub"></p>
107
129
  <dl id="dRows"></dl>
108
- <h3>HISTÓRICO</h3>
109
- <ul id="dHist"></ul>
130
+ <h3>CONVERSA</h3>
131
+ <div id="dChat"></div>
132
+ <form id="dSend">
133
+ <input id="dSendText" type="text" autocomplete="off" placeholder="mensagem para esta sessão">
134
+ <button id="dSendBtn" type="submit">Enviar</button>
135
+ </form>
136
+ <p id="dSendMsg" role="status"></p>
137
+ <button id="dArchive" type="button" style="display:none">Marcar como concluída</button>
110
138
  </aside>
111
139
  <script>
112
140
  "use strict";
@@ -114,10 +142,11 @@ var LOGO_ANTHROPIC = '<svg viewBox="0 0 24 24"><path d="M17.304 3.541h-3.672l6.6
114
142
  var LOGO_OPENAI = '<svg viewBox="0 0 24 24"><path d="M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z"/></svg>';
115
143
  var LOGO_TERM = '<svg viewBox="0 0 24 24" fill="none" stroke="#f5f5f7" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2.5" y="4" width="19" height="16" rx="4"/><path d="M7 9.5l3 3-3 3M12.5 15.5H17"/></svg>';
116
144
  var LOGO_ORCH = '<svg viewBox="0 0 24 24" fill="none" stroke="#0a84ff" stroke-width="2" stroke-linecap="round"><circle cx="12" cy="5" r="2.4" fill="#0a84ff" stroke="none"/><circle cx="5" cy="19" r="2.4" fill="#0a84ff" stroke="none"/><circle cx="19" cy="19" r="2.4" fill="#0a84ff" stroke="none"/><path d="M12 7.5 5.8 16.7M12 7.5l6.2 9.2M7.4 19h9.2"/></svg>';
145
+ var LOGO_OPENCODE = '<svg viewBox="0 0 24 24"><path fill-rule="evenodd" d="M16 6H8v12h8V6zm4 16H4V2h16v20z"/></svg>';
117
146
  function logoFor(agent) {
118
147
  if (agent === "claude") return LOGO_ANTHROPIC;
119
148
  if (agent === "codex") return LOGO_OPENAI;
120
- if (agent === "opencode") return LOGO_ORCH;
149
+ if (agent === "opencode") return LOGO_OPENCODE;
121
150
  return LOGO_TERM;
122
151
  }
123
152
  var AGENT_NAME = { claude: "Claude", codex: "Codex", opencode: "OpenCode", omp: "OMP", antigravity: "Antigravity" };
@@ -253,7 +282,7 @@ function makeNodeEl(n) {
253
282
  row.className = "row";
254
283
  var logo = document.createElement("span");
255
284
  logo.className = "logo";
256
- logo.innerHTML = logoFor(n.agent);
285
+ logo.innerHTML = n.isOrch ? LOGO_ORCH : logoFor(n.agent);
257
286
  row.appendChild(logo);
258
287
  var dot = document.createElement("i");
259
288
  row.appendChild(dot);
@@ -267,13 +296,14 @@ function makeNodeEl(n) {
267
296
  var act = document.createElement("div");
268
297
  act.className = "act";
269
298
  el.appendChild(act);
270
- n.el = el; n.dotEl = dot; n.nameEl = name; n.subEl = sub; n.actEl = act;
299
+ n.el = el; n.logoEl = logo; n.dotEl = dot; n.nameEl = name; n.subEl = sub; n.actEl = act;
271
300
  el.addEventListener("mousedown", function (ev) { startDragNode(ev, n); });
272
301
  nodesLayer.appendChild(el);
273
302
  paintNode(n);
274
303
  }
275
304
  function paintNode(n) {
276
305
  n.dotEl.className = "dot " + (STATUS_DOT[n.status] || "done");
306
+ if (n.logoEl) n.logoEl.innerHTML = n.isOrch ? LOGO_ORCH : logoFor(n.agent);
277
307
  n.nameEl.textContent = n.isOrch ? "Orquestrador" : agentName(n.agent);
278
308
  if (n.isOrch) {
279
309
  n.subEl.style.display = "";
@@ -410,6 +440,11 @@ function reconcile(sessions) {
410
440
  if (Date.now() > n.liveUntil) n.activity = baseActivity(s);
411
441
  paintNode(n);
412
442
  seen[s.id] = true;
443
+ if (detailId === s.id) {
444
+ detailStatus = s.status;
445
+ detailOrigin = s.origin || null;
446
+ paintSendState();
447
+ }
413
448
  });
414
449
  for (var id in nodesById) {
415
450
  if (!seen[id]) {
@@ -538,6 +573,12 @@ function onEvent(n, ev) {
538
573
  touch(n, "respondendo");
539
574
  feed(agentName(n.agent), String(ev.content || "").slice(0, 90));
540
575
  }
576
+ } else if (ev.type === "message.queued") {
577
+ touch(n, "mensagem na fila");
578
+ feed(agentName(n.agent), "mensagem na fila");
579
+ if (n.id && n.id === detailId) { detailPending = ev.prompt || true; paintSendState(); }
580
+ } else if (ev.type === "turn.started") {
581
+ if (n.id && n.id === detailId && detailPending) { detailPending = null; paintSendState(); }
541
582
  } else if (ev.type === "turn.completed") {
542
583
  feed(agentName(n.agent), "etapa concluída");
543
584
  } else if (ev.type === "file.changed") {
@@ -553,6 +594,9 @@ function onEvent(n, ev) {
553
594
  touch(n, ev.type === "session.failed" ? ("falhou: " + (ev.error || "")) : "concluída");
554
595
  paintSummary();
555
596
  feed(agentName(n.agent), ev.type === "session.failed" ? "falhou" : "concluída");
597
+ // Stop/release cancels the queue server-side: drop a stale "na fila"
598
+ // hint on the open detail, mirroring the turn.started branch.
599
+ if (n.id && n.id === detailId && detailPending) { detailPending = null; paintSendState(); }
556
600
  if (n.es) { try { n.es.close(); } catch (e) {} n.es = null; }
557
601
  }
558
602
  }
@@ -754,6 +798,10 @@ window.addEventListener("keydown", function (ev) {
754
798
  if (ev.key === "Escape") closeDetail();
755
799
  });
756
800
  window.addEventListener("wheel", function (ev) {
801
+ // O painel de detalhe tem scroll proprio (#detail e #dChat): o zoom do
802
+ // canvas so vale fora dele, senao rolar o chat daria zoom na cena.
803
+ var t = ev.target;
804
+ if (t && typeof t.closest === "function" && t.closest("#detail")) return;
757
805
  ev.preventDefault();
758
806
  var before = toWorld(ev.clientX, ev.clientY);
759
807
  cam.zoom = Math.min(2.2, Math.max(0.45, cam.zoom * (ev.deltaY < 0 ? 1.1 : 0.9)));
@@ -764,17 +812,20 @@ window.addEventListener("wheel", function (ev) {
764
812
  function syncUrl() {
765
813
  try {
766
814
  var qp = new URLSearchParams(window.location.search);
767
- if (hideDone) qp.set("hide", "1"); else qp.delete("hide");
815
+ // Padrão é esconder concluídas: a URL limpa É o estado escondido, e
816
+ // quem quiser ver tudo marca hide=0 explicitamente.
817
+ if (hideDone) qp.delete("hide"); else qp.set("hide", "0");
768
818
  if (!motion) qp.set("motion", "0"); else qp.delete("motion");
769
819
  var qs = qp.toString();
770
820
  window.history.replaceState(null, "", window.location.pathname + (qs ? "?" + qs : ""));
771
821
  } catch (e) {}
772
822
  }
773
- // Filtros sobrevivem ao F5: o estado mora na query string (?hide=1&motion=0).
774
- var urlHide = false, urlMotion = true;
823
+ // Filtros sobrevivem ao F5: o estado mora na query string (?hide=0&motion=0).
824
+ // hideDone nasce true: concluídas ficam fora até alguém pedir hide=0.
825
+ var urlHide = true, urlMotion = true;
775
826
  try {
776
827
  var qp0 = new URLSearchParams(window.location.search);
777
- urlHide = qp0.get("hide") === "1";
828
+ urlHide = qp0.get("hide") !== "0";
778
829
  urlMotion = qp0.get("motion") !== "0";
779
830
  } catch (e) {}
780
831
  motion = urlMotion;
@@ -808,31 +859,44 @@ document.getElementById("btnMotion").classList.toggle("on", motion);
808
859
  document.getElementById("btnHide").classList.toggle("on", hideDone);
809
860
  document.getElementById("btnHide").title = hideDone ? "Mostrar concluídas" : "Ocultar concluídas";
810
861
 
811
- function fmtTime(iso) {
812
- try {
813
- var d = new Date(iso);
814
- return d.toLocaleTimeString("pt-BR", { hour: "2-digit", minute: "2-digit", second: "2-digit" });
815
- } catch (e) { return ""; }
816
- }
817
- function fmtEvent(ev) {
818
- if (!ev || !ev.type) return "";
819
- if (ev.type === "text.delta") return null;
820
- if (ev.type === "tool.started") return "Começou: " + toolLabel(ev.tool);
821
- if (ev.type === "tool.completed") {
822
- var bad = ev.tool && ev.tool.success === false;
823
- return "Terminou: " + toolLabel(ev.tool) + (bad ? " (com erro)" : "");
824
- }
825
- if (ev.type === "message" && ev.role === "assistant") return "Respondeu: " + String(ev.content || "").slice(0, 120);
826
- if (ev.type === "message") return null;
827
- if (ev.type === "turn.started") return "Nova etapa";
828
- if (ev.type === "turn.completed") return "Etapa concluída";
829
- if (ev.type === "file.changed") return "Mudou " + (ev.path || "um arquivo");
830
- if (ev.type === "permission.requested") return "Pediu aprovação: " + (ev.tool || "uma ação");
831
- if (ev.type === "permission.resolved") return ev.approved ? "Aprovado" : "Negado";
832
- if (ev.type === "session.completed") return "Sessão concluída";
833
- if (ev.type === "session.failed") return "Sessão falhou: " + (ev.error || "");
834
- if (ev.type === "session.started") return "Sessão iniciada";
835
- return null;
862
+ /* Transcrição da sessão: a mensagem do usuário mora em turn.started.prompt
863
+ (o daemon anexa no turno inicial e a cada send), a resposta em
864
+ message(role=assistant) com content completo. Ferramentas viram linhas
865
+ compactas para a conversa continuar legível. */
866
+ var detailId = null, detailStatus = null, detailOrigin = null, detailPending = null;
867
+ var sendInFlight = false, sendBlockedNote = false;
868
+ function chatMsg(who, text, cls) {
869
+ var d = document.createElement("div");
870
+ d.className = "msg " + cls;
871
+ var w = document.createElement("span");
872
+ w.className = "who";
873
+ w.textContent = who;
874
+ d.appendChild(w);
875
+ d.appendChild(document.createTextNode(text));
876
+ return d;
877
+ }
878
+ function chatEvt(text) {
879
+ var d = document.createElement("div");
880
+ d.className = "evt";
881
+ d.textContent = text;
882
+ return d;
883
+ }
884
+ /* Input e botão seguem o estado do daemon: ocupada trava, origin=open
885
+ (TUI interativa) trava sempre. Mensagem de sucesso não é apagada aqui. */
886
+ function paintSendState() {
887
+ var input = document.getElementById("dSendText");
888
+ var btn = document.getElementById("dSendBtn");
889
+ var msg = document.getElementById("dSendMsg");
890
+ if (!detailId) return;
891
+ // origin=open (TUI interativa) trava sempre; working/starting entra na
892
+ // fila em vez de travar. Mensagem de sucesso não é apagada aqui.
893
+ var reason = "";
894
+ if (detailOrigin === "open") reason = "sessão interativa não aceita mensagens";
895
+ input.disabled = sendInFlight || reason !== "";
896
+ btn.disabled = sendInFlight || reason !== "";
897
+ if (reason) { msg.textContent = reason; sendBlockedNote = true; }
898
+ else if (detailPending) { msg.textContent = "1 mensagem na fila — envia quando o turno terminar"; sendBlockedNote = true; }
899
+ else if (sendBlockedNote) { msg.textContent = ""; sendBlockedNote = false; }
836
900
  }
837
901
  function row(dt, dd) {
838
902
  var d = document.createElement("dt");
@@ -842,20 +906,33 @@ function row(dt, dd) {
842
906
  return [d, v];
843
907
  }
844
908
  function closeDetail() {
909
+ detailId = null;
910
+ detailStatus = null;
911
+ detailOrigin = null;
912
+ detailPending = null;
845
913
  document.getElementById("detail").classList.remove("open");
846
914
  }
847
915
  document.getElementById("detailClose").onclick = closeDetail;
848
916
  function selectSession(id) {
849
- fetch("api/sessions/" + encodeURIComponent(id)).then(function (r) { return r.json(); }).then(function (j) {
917
+ // /logs traz até 1000 eventos; /get limitaria a transcrição aos 10 últimos.
918
+ fetch("api/sessions/" + encodeURIComponent(id) + "/logs").then(function (r) { return r.json(); }).then(function (j) {
850
919
  var s = j.session || j;
851
920
  if (!s || !s.id) return;
921
+ detailId = s.id;
922
+ detailStatus = s.status;
923
+ detailOrigin = s.origin || null;
924
+ detailPending = s.pendingMessage || null;
925
+ // Interrupted rows are actionable again: offer archival. Every other
926
+ // status keeps the button hidden.
927
+ document.getElementById("dArchive").style.display = s.status === "interrupted" ? "" : "none";
852
928
  var title = document.getElementById("dTitle");
853
929
  title.innerHTML = "";
930
+ var isOrch = (nodesById[s.id] && nodesById[s.id].isOrch) || (s.id && s.id === s.runId);
854
931
  var logo = document.createElement("span");
855
932
  logo.className = "logo";
856
- logo.innerHTML = logoFor(s.agent);
933
+ logo.innerHTML = isOrch ? LOGO_ORCH : logoFor(s.agent);
857
934
  title.appendChild(logo);
858
- title.appendChild(document.createTextNode(agentName(s.agent) + " · " + (s.name || s.id)));
935
+ title.appendChild(document.createTextNode((isOrch ? "Orquestrador" : agentName(s.agent)) + " · " + (s.name || s.id)));
859
936
  document.getElementById("dSub").textContent = (STATUS_HUMAN[s.status] || s.status) +
860
937
  (s.createdAt ? " · começou " + timeAgo(s.createdAt) : "");
861
938
  var rows = document.getElementById("dRows");
@@ -870,8 +947,9 @@ function selectSession(id) {
870
947
  ];
871
948
  if (s.pid) items.push(["PID", String(s.pid)]);
872
949
  if (s.updatedAt) items.push(["Atualizado", timeAgo(s.updatedAt)]);
950
+ var events = j.events || [];
873
951
  var tools = 0, msgs = 0;
874
- (j.events || []).forEach(function (ev) {
952
+ events.forEach(function (ev) {
875
953
  if (!ev) return;
876
954
  if (ev.type === "tool.completed") tools++;
877
955
  if (ev.type === "message" && ev.role === "assistant") msgs++;
@@ -881,29 +959,102 @@ function selectSession(id) {
881
959
  items.forEach(function (kv) {
882
960
  row(kv[0], kv[1]).forEach(function (el) { rows.appendChild(el); });
883
961
  });
884
- var hist = document.getElementById("dHist");
885
- hist.innerHTML = "";
886
- var shown = 0;
887
- for (var i = (j.events || []).length - 1; i >= 0 && shown < 30; i--) {
888
- var line = fmtEvent(j.events[i]);
889
- if (!line) continue;
890
- var li = document.createElement("li");
891
- var ts = document.createElement("span");
892
- ts.className = "ts";
893
- ts.textContent = fmtTime(j.events[i].timestamp) + " ";
894
- li.appendChild(ts);
895
- li.appendChild(document.createTextNode(line));
896
- hist.appendChild(li);
897
- shown++;
962
+ var chat = document.getElementById("dChat");
963
+ chat.innerHTML = "";
964
+ // Últimos 200 eventos bastam: render além disso só pesa o DOM.
965
+ var start = Math.max(0, events.length - 200);
966
+ for (var i = start; i < events.length; i++) {
967
+ var ev = events[i];
968
+ if (!ev || !ev.type) continue;
969
+ if (ev.type === "turn.started" && ev.prompt) {
970
+ chat.appendChild(chatMsg("você", String(ev.prompt), "user"));
971
+ } else if (ev.type === "message.queued" && ev.prompt) {
972
+ chat.appendChild(chatMsg("você", String(ev.prompt) + " (na fila)", "user"));
973
+ } else if (ev.type === "message" && ev.role === "assistant" && ev.content) {
974
+ chat.appendChild(chatMsg(agentName(s.agent), String(ev.content), "assistant"));
975
+ } else if (ev.type === "tool.started") {
976
+ chat.appendChild(chatEvt("começou: " + toolLabel(ev.tool)));
977
+ } else if (ev.type === "tool.completed") {
978
+ var bad = ev.tool && ev.tool.success === false;
979
+ chat.appendChild(chatEvt("terminou: " + toolLabel(ev.tool) + (bad ? " (com erro)" : "")));
980
+ } else if (ev.type === "permission.requested") {
981
+ chat.appendChild(chatEvt("pediu aprovação: " + (ev.tool || "uma ação")));
982
+ } else if (ev.type === "file.changed") {
983
+ chat.appendChild(chatEvt("mudou " + (ev.path || "um arquivo")));
984
+ }
898
985
  }
899
- if (!shown) {
900
- var empty = document.createElement("li");
901
- empty.textContent = "Sem histórico ainda.";
902
- hist.appendChild(empty);
986
+ if (!chat.children.length) {
987
+ var empty = document.createElement("div");
988
+ empty.className = "empty";
989
+ empty.textContent = "Sem mensagens ainda.";
990
+ chat.appendChild(empty);
903
991
  }
992
+ chat.scrollTop = chat.scrollHeight;
904
993
  document.getElementById("detail").classList.add("open");
994
+ paintSendState();
905
995
  }).catch(function () {});
906
996
  }
997
+ document.getElementById("dSend").addEventListener("submit", function (ev) {
998
+ ev.preventDefault();
999
+ if (!detailId || sendInFlight) return;
1000
+ var input = document.getElementById("dSendText");
1001
+ var text = input.value.trim();
1002
+ if (!text) return;
1003
+ var msg = document.getElementById("dSendMsg");
1004
+ var btn = document.getElementById("dSendBtn");
1005
+ sendInFlight = true;
1006
+ input.disabled = true;
1007
+ btn.disabled = true;
1008
+ msg.textContent = "enviando...";
1009
+ fetch("api/sessions/" + encodeURIComponent(detailId) + "/send", {
1010
+ method: "POST",
1011
+ headers: { "content-type": "application/json" },
1012
+ body: JSON.stringify({ message: text })
1013
+ }).then(function (r) {
1014
+ return r.json().then(function (j) { return { ok: r.ok, body: j }; });
1015
+ }).then(function (out) {
1016
+ if (out.ok) {
1017
+ input.value = "";
1018
+ if (out.body && out.body.queued) {
1019
+ detailPending = text;
1020
+ msg.textContent = "mensagem na fila — envia quando o turno terminar";
1021
+ } else {
1022
+ detailPending = null;
1023
+ msg.textContent = "mensagem enviada — nova etapa começou";
1024
+ }
1025
+ poll();
1026
+ } else {
1027
+ msg.textContent = (out.body && out.body.error) || "não deu para enviar";
1028
+ }
1029
+ }).catch(function () {
1030
+ msg.textContent = "daemon fora do ar?";
1031
+ }).then(function () {
1032
+ sendInFlight = false;
1033
+ paintSendState();
1034
+ });
1035
+ });
1036
+ document.getElementById("dArchive").addEventListener("click", function () {
1037
+ if (!detailId) return;
1038
+ var msg = document.getElementById("dSendMsg");
1039
+ var btn = document.getElementById("dArchive");
1040
+ btn.disabled = true;
1041
+ msg.textContent = "arquivando...";
1042
+ fetch("api/sessions/" + encodeURIComponent(detailId) + "/release", { method: "POST" }).then(function (r) {
1043
+ return r.json().then(function (j) { return { ok: r.ok, body: j }; });
1044
+ }).then(function (out) {
1045
+ if (out.ok) {
1046
+ msg.textContent = "sessão arquivada como concluída";
1047
+ selectSession(detailId);
1048
+ poll();
1049
+ } else {
1050
+ msg.textContent = (out.body && out.body.error) || "não deu para arquivar";
1051
+ }
1052
+ }).catch(function () {
1053
+ msg.textContent = "daemon fora do ar?";
1054
+ }).then(function () {
1055
+ btn.disabled = false;
1056
+ });
1057
+ });
907
1058
 
908
1059
  function poll() {
909
1060
  fetch("api/sessions").then(function (r) { return r.json(); }).then(function (j) {
@@ -1 +1 @@
1
- {"version":3,"file":"canvas-page.js","sourceRoot":"","sources":["../../src/web/canvas-page.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,WAAW,GAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAg5B3B,CAAC"}
1
+ {"version":3,"file":"canvas-page.js","sourceRoot":"","sources":["../../src/web/canvas-page.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,WAAW,GAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAuiC3B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codedeck",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "CodeDeck, a local runtime for coding agents with session management, process supervision, event normalization, and git isolation",
5
5
  "type": "module",
6
6
  "bin": {