@sentropic/h2a 0.81.0 → 0.82.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -0
- package/dist/bin-routing.d.ts +13 -0
- package/dist/bin-routing.d.ts.map +1 -0
- package/dist/bin-routing.js +46 -0
- package/dist/bin-routing.js.map +1 -0
- package/dist/bin.js +40 -11
- package/dist/bin.js.map +1 -1
- package/dist/cli-contract.d.ts.map +1 -1
- package/dist/cli-contract.js +22 -6
- package/dist/cli-contract.js.map +1 -1
- package/dist/cli.d.ts +16 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +197 -11
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +3 -1
- package/dist/mcp.js.map +1 -1
- package/dist/runtime/canevas/adapter.d.ts +11 -0
- package/dist/runtime/canevas/adapter.d.ts.map +1 -0
- package/dist/runtime/canevas/adapter.js +22 -0
- package/dist/runtime/canevas/adapter.js.map +1 -0
- package/dist/runtime/canevas/aggregate.d.ts +56 -0
- package/dist/runtime/canevas/aggregate.d.ts.map +1 -0
- package/dist/runtime/canevas/aggregate.js +84 -0
- package/dist/runtime/canevas/aggregate.js.map +1 -0
- package/dist/runtime/canevas/answer.d.ts +21 -0
- package/dist/runtime/canevas/answer.d.ts.map +1 -0
- package/dist/runtime/canevas/answer.js +62 -0
- package/dist/runtime/canevas/answer.js.map +1 -0
- package/dist/runtime/canevas/app.d.ts +19 -0
- package/dist/runtime/canevas/app.d.ts.map +1 -0
- package/dist/runtime/canevas/app.js +49 -0
- package/dist/runtime/canevas/app.js.map +1 -0
- package/dist/runtime/canevas/gather.d.ts +3 -0
- package/dist/runtime/canevas/gather.d.ts.map +1 -0
- package/dist/runtime/canevas/gather.js +39 -0
- package/dist/runtime/canevas/gather.js.map +1 -0
- package/dist/runtime/canevas/reply.d.ts +27 -0
- package/dist/runtime/canevas/reply.d.ts.map +1 -0
- package/dist/runtime/canevas/reply.js +31 -0
- package/dist/runtime/canevas/reply.js.map +1 -0
- package/dist/runtime/canevas/serve.d.ts +11 -0
- package/dist/runtime/canevas/serve.d.ts.map +1 -0
- package/dist/runtime/canevas/serve.js +42 -0
- package/dist/runtime/canevas/serve.js.map +1 -0
- package/dist/runtime/canevas/ui.d.ts +2 -0
- package/dist/runtime/canevas/ui.d.ts.map +1 -0
- package/dist/runtime/canevas/ui.js +124 -0
- package/dist/runtime/canevas/ui.js.map +1 -0
- package/dist/runtime/loop/engine/adapters.d.ts +91 -0
- package/dist/runtime/loop/engine/adapters.d.ts.map +1 -0
- package/dist/runtime/loop/engine/adapters.js +292 -0
- package/dist/runtime/loop/engine/adapters.js.map +1 -0
- package/dist/runtime/loop/engine/decision.d.ts +71 -0
- package/dist/runtime/loop/engine/decision.d.ts.map +1 -0
- package/dist/runtime/loop/engine/decision.js +141 -0
- package/dist/runtime/loop/engine/decision.js.map +1 -0
- package/dist/runtime/loop/engine/execute.d.ts +33 -0
- package/dist/runtime/loop/engine/execute.d.ts.map +1 -0
- package/dist/runtime/loop/engine/execute.js +69 -0
- package/dist/runtime/loop/engine/execute.js.map +1 -0
- package/dist/runtime/loop/engine/tick.d.ts +27 -0
- package/dist/runtime/loop/engine/tick.d.ts.map +1 -0
- package/dist/runtime/loop/engine/tick.js +72 -0
- package/dist/runtime/loop/engine/tick.js.map +1 -0
- package/dist/runtime/loop/index.d.ts +14 -0
- package/dist/runtime/loop/index.d.ts.map +1 -1
- package/dist/runtime/loop/index.js +23 -0
- package/dist/runtime/loop/index.js.map +1 -1
- package/dist/runtime/mcp/handlers.d.ts +12 -0
- package/dist/runtime/mcp/handlers.d.ts.map +1 -1
- package/dist/runtime/mcp/handlers.js +42 -0
- package/dist/runtime/mcp/handlers.js.map +1 -1
- package/dist/runtime/mcp/server.d.ts.map +1 -1
- package/dist/runtime/mcp/server.js +5 -1
- package/dist/runtime/mcp/server.js.map +1 -1
- package/dist/runtime/mcp/tools.d.ts.map +1 -1
- package/dist/runtime/mcp/tools.js +22 -0
- package/dist/runtime/mcp/tools.js.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare const CANEVAS_HTML = "<!doctype html>\n<html lang=\"fr\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>h2a canevas \u2014 d\u00E9cisions</title>\n<style>\n :root{--bg:#0d1117;--panel:#161b22;--border:#30363d;--fg:#e6edf3;--muted:#8b949e;--accent:#3b82f6;--alert:#f85149;--decide:#d29922;--advise:#3fb950}\n *{box-sizing:border-box}\n body{margin:0;font:14px/1.5 -apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,sans-serif;background:var(--bg);color:var(--fg)}\n header{padding:14px 20px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:12px}\n header h1{font-size:15px;margin:0;font-weight:600}\n .count{color:var(--muted);font-size:13px}\n main{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.2fr);gap:16px;padding:16px 20px;height:calc(100vh - 52px)}\n .col{min-width:0;display:flex;flex-direction:column}\n .card{background:var(--panel);border:1px solid var(--border);border-radius:10px;padding:18px}\n .chan{display:inline-block;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;padding:2px 8px;border-radius:999px}\n .chan.alert{background:rgba(248,81,73,.15);color:var(--alert)}\n .chan.decide{background:rgba(210,153,34,.15);color:var(--decide)}\n .chan.advise{background:rgba(63,185,80,.15);color:var(--advise)}\n .q{font-size:18px;font-weight:600;margin:14px 0}\n .meta{color:var(--muted);font-size:12px;display:flex;flex-wrap:wrap;gap:12px}\n .nav{display:flex;align-items:center;gap:10px;margin-top:auto;padding-top:16px}\n button{font:inherit;background:var(--panel);color:var(--fg);border:1px solid var(--border);border-radius:8px;padding:8px 14px;cursor:pointer}\n button:hover:not(:disabled){border-color:var(--accent)}\n button:disabled{opacity:.5;cursor:not-allowed}\n .answers{display:flex;gap:8px;margin-top:16px;flex-wrap:wrap}\n .answers button{border-color:var(--accent)}\n .answerStatus{margin-top:10px;font-size:13px;min-height:18px}\n .answerStatus.ok{color:var(--advise)}\n .answerStatus.err{color:var(--alert)}\n .pane{background:#010409;border:1px solid var(--border);border-radius:10px;padding:12px;overflow:auto;flex:1;display:flex;flex-direction:column}\n .pane .label{color:var(--muted);font-size:11px;margin-bottom:8px;text-transform:uppercase;letter-spacing:.05em}\n .pane pre{margin:0;font:12px/1.45 ui-monospace,SFMono-Regular,Menlo,monospace;white-space:pre-wrap;word-break:break-word;color:#c9d1d9}\n .empty{color:var(--muted);text-align:center;margin-top:40px}\n</style>\n</head>\n<body>\n<header>\n <h1>h2a canevas \u2462</h1>\n <span class=\"count\" id=\"count\">\u2026</span>\n <span style=\"margin-left:auto;color:var(--muted);font-size:12px\">\u2190 \u2192 pour naviguer</span>\n</header>\n<main>\n <section class=\"col\">\n <div id=\"card\" class=\"card\"><div class=\"empty\">Chargement\u2026</div></div>\n <div class=\"nav\">\n <button id=\"prev\">\u2190 Pr\u00E9c\u00E9dent</button>\n <button id=\"next\">Suivant \u2192</button>\n <span class=\"count\" id=\"pos\"></span>\n </div>\n <div class=\"answerStatus\" id=\"answerStatus\" role=\"status\" aria-live=\"polite\"></div>\n </section>\n <section class=\"col\">\n <div class=\"pane\"><div class=\"label\" id=\"paneLabel\">Session CLI</div><pre id=\"pane\">\u2014</pre></div>\n </section>\n</main>\n<script>\n(function(){\n // Per-run write token, injected same-origin at GET /. A cross-origin page can\n // neither read it nor (CORS-closed) reach /api \u2014 so only this page can POST.\n var TOKEN=\"__CANEVAS_TOKEN__\";\n var decisions=[],idx=0,paneTimer=null;\n function esc(s){return String(s==null?\"\":s).replace(/[&<>]/g,function(c){return{\"&\":\"&\",\"<\":\"<\",\">\":\">\"}[c]})}\n function setStatus(msg,ok){var s=document.getElementById(\"answerStatus\");s.textContent=msg;s.className=\"answerStatus\"+(msg?(ok?\" ok\":\" err\"):\"\")}\n async function answer(decisionId,answerId,btn){\n setStatus(\"Envoi de la r\u00E9ponse\u2026\",true);\n if(btn){btn.disabled=true}\n try{\n var r=await fetch(\"/api/decisions/\"+encodeURIComponent(decisionId)+\"/answer\",{method:\"POST\",headers:{\"content-type\":\"application/json\",\"x-canevas-token\":TOKEN},body:JSON.stringify({answerId:answerId})});\n var j={};try{j=await r.json()}catch(e){}\n if(r.ok){setStatus(\"\u2713 R\u00E9ponse \u00AB \"+answerId+\" \u00BB transmise \u00E0 l'agent.\",true)}\n else{setStatus(\"\u2717 \"+(j&&j.error?j.error:(\"\u00E9chec (\"+r.status+\")\")),false)}\n }catch(e){setStatus(\"\u2717 erreur r\u00E9seau \u2014 r\u00E9essayez.\",false)}\n load();\n }\n async function load(){\n try{var r=await fetch(\"/api/decisions\");var j=await r.json();decisions=(j.decisions||[])}catch(e){decisions=[]}\n if(idx>=decisions.length)idx=Math.max(0,decisions.length-1);\n render();\n }\n function render(){\n document.getElementById(\"count\").textContent=decisions.length+\" d\u00E9cision\"+(decisions.length>1?\"s\":\"\")+\" en attente\";\n var card=document.getElementById(\"card\");\n if(!decisions.length){card.innerHTML='<div class=\"empty\">Aucune d\u00E9cision en attente \uD83C\uDF89</div>';document.getElementById(\"pos\").textContent=\"\";setPane(null);return}\n var d=decisions[idx];\n var opts=(d.options&&d.options.length)?d.options:[{id:\"go\",label:\"Valider\"},{id:\"veto\",label:\"Refuser\"},{id:\"defer\",label:\"Diff\u00E9rer\"}];\n card.innerHTML=\n '<span class=\"chan '+esc(d.channel||\"\")+'\">'+esc(d.channel||\"d\u00E9cision\")+'</span>'+\n '<div class=\"q\">'+esc(d.question)+'</div>'+\n '<div class=\"meta\"><span>agent: '+esc(d.instance)+'</span>'+(d.workspace?'<span>ws: '+esc(d.workspace)+'</span>':'')+(d.createdAt?'<span>'+esc(d.createdAt)+'</span>':'')+'</div>'+\n '<div class=\"answers\">'+opts.map(function(o){return '<button class=\"ans\" data-a=\"'+esc(o.id)+'\">'+esc(o.label)+'</button>'}).join(\"\")+'</div>';\n Array.prototype.forEach.call(card.querySelectorAll(\".ans\"),function(btn){\n btn.onclick=function(){answer(d.id,btn.getAttribute(\"data-a\"),btn)};\n });\n document.getElementById(\"pos\").textContent=(idx+1)+\" / \"+decisions.length;\n setPane(d.sessionRef&&d.sessionRef.tmuxName?d.sessionRef.tmuxName:null);\n }\n function setPane(tmuxName){\n if(paneTimer){clearInterval(paneTimer);paneTimer=null}\n var pre=document.getElementById(\"pane\"),label=document.getElementById(\"paneLabel\");\n if(!tmuxName){pre.textContent=\"\u2014 (pas de session tmux li\u00E9e)\";label.textContent=\"Session CLI\";return}\n label.textContent=\"Session CLI \u00B7 \"+tmuxName;\n async function poll(){\n try{var r=await fetch(\"/api/sessions/\"+encodeURIComponent(tmuxName)+\"/pane?lines=200\");var j=await r.json();\n pre.textContent=j.degraded?\"(runtime indisponible \u2014 capturePane degraded)\":(j.text||\"(pane vide)\")}catch(e){pre.textContent=\"(erreur de lecture du pane)\"}\n }\n poll();paneTimer=setInterval(poll,2000);\n }\n function go(delta){if(!decisions.length)return;idx=(idx+delta+decisions.length)%decisions.length;render()}\n document.getElementById(\"prev\").onclick=function(){go(-1)};\n document.getElementById(\"next\").onclick=function(){go(1)};\n document.addEventListener(\"keydown\",function(e){if(e.key===\"ArrowLeft\")go(-1);else if(e.key===\"ArrowRight\")go(1)});\n load();setInterval(load,5000);\n})();\n</script>\n</body>\n</html>";
|
|
2
|
+
//# sourceMappingURL=ui.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../../../src/runtime/canevas/ui.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,YAAY,02OAqHjB,CAAC"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
// Canevas ③ — self-hosted UI (served at GET / by the local Hono server, NOT a
|
|
2
|
+
// claude.ai Artifact: an Artifact cannot reach localhost/api). Self-contained:
|
|
3
|
+
// inline CSS + vanilla JS, no CDN. tranche-3b — the answer buttons are LIVE: they
|
|
4
|
+
// POST the human decision through the guarded reply-bridge, carrying the per-run
|
|
5
|
+
// write token injected same-origin at GET / (`__CANEVAS_TOKEN__`).
|
|
6
|
+
export const CANEVAS_HTML = `<!doctype html>
|
|
7
|
+
<html lang="fr">
|
|
8
|
+
<head>
|
|
9
|
+
<meta charset="utf-8">
|
|
10
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
11
|
+
<title>h2a canevas — décisions</title>
|
|
12
|
+
<style>
|
|
13
|
+
:root{--bg:#0d1117;--panel:#161b22;--border:#30363d;--fg:#e6edf3;--muted:#8b949e;--accent:#3b82f6;--alert:#f85149;--decide:#d29922;--advise:#3fb950}
|
|
14
|
+
*{box-sizing:border-box}
|
|
15
|
+
body{margin:0;font:14px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;background:var(--bg);color:var(--fg)}
|
|
16
|
+
header{padding:14px 20px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:12px}
|
|
17
|
+
header h1{font-size:15px;margin:0;font-weight:600}
|
|
18
|
+
.count{color:var(--muted);font-size:13px}
|
|
19
|
+
main{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.2fr);gap:16px;padding:16px 20px;height:calc(100vh - 52px)}
|
|
20
|
+
.col{min-width:0;display:flex;flex-direction:column}
|
|
21
|
+
.card{background:var(--panel);border:1px solid var(--border);border-radius:10px;padding:18px}
|
|
22
|
+
.chan{display:inline-block;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;padding:2px 8px;border-radius:999px}
|
|
23
|
+
.chan.alert{background:rgba(248,81,73,.15);color:var(--alert)}
|
|
24
|
+
.chan.decide{background:rgba(210,153,34,.15);color:var(--decide)}
|
|
25
|
+
.chan.advise{background:rgba(63,185,80,.15);color:var(--advise)}
|
|
26
|
+
.q{font-size:18px;font-weight:600;margin:14px 0}
|
|
27
|
+
.meta{color:var(--muted);font-size:12px;display:flex;flex-wrap:wrap;gap:12px}
|
|
28
|
+
.nav{display:flex;align-items:center;gap:10px;margin-top:auto;padding-top:16px}
|
|
29
|
+
button{font:inherit;background:var(--panel);color:var(--fg);border:1px solid var(--border);border-radius:8px;padding:8px 14px;cursor:pointer}
|
|
30
|
+
button:hover:not(:disabled){border-color:var(--accent)}
|
|
31
|
+
button:disabled{opacity:.5;cursor:not-allowed}
|
|
32
|
+
.answers{display:flex;gap:8px;margin-top:16px;flex-wrap:wrap}
|
|
33
|
+
.answers button{border-color:var(--accent)}
|
|
34
|
+
.answerStatus{margin-top:10px;font-size:13px;min-height:18px}
|
|
35
|
+
.answerStatus.ok{color:var(--advise)}
|
|
36
|
+
.answerStatus.err{color:var(--alert)}
|
|
37
|
+
.pane{background:#010409;border:1px solid var(--border);border-radius:10px;padding:12px;overflow:auto;flex:1;display:flex;flex-direction:column}
|
|
38
|
+
.pane .label{color:var(--muted);font-size:11px;margin-bottom:8px;text-transform:uppercase;letter-spacing:.05em}
|
|
39
|
+
.pane pre{margin:0;font:12px/1.45 ui-monospace,SFMono-Regular,Menlo,monospace;white-space:pre-wrap;word-break:break-word;color:#c9d1d9}
|
|
40
|
+
.empty{color:var(--muted);text-align:center;margin-top:40px}
|
|
41
|
+
</style>
|
|
42
|
+
</head>
|
|
43
|
+
<body>
|
|
44
|
+
<header>
|
|
45
|
+
<h1>h2a canevas ③</h1>
|
|
46
|
+
<span class="count" id="count">…</span>
|
|
47
|
+
<span style="margin-left:auto;color:var(--muted);font-size:12px">← → pour naviguer</span>
|
|
48
|
+
</header>
|
|
49
|
+
<main>
|
|
50
|
+
<section class="col">
|
|
51
|
+
<div id="card" class="card"><div class="empty">Chargement…</div></div>
|
|
52
|
+
<div class="nav">
|
|
53
|
+
<button id="prev">← Précédent</button>
|
|
54
|
+
<button id="next">Suivant →</button>
|
|
55
|
+
<span class="count" id="pos"></span>
|
|
56
|
+
</div>
|
|
57
|
+
<div class="answerStatus" id="answerStatus" role="status" aria-live="polite"></div>
|
|
58
|
+
</section>
|
|
59
|
+
<section class="col">
|
|
60
|
+
<div class="pane"><div class="label" id="paneLabel">Session CLI</div><pre id="pane">—</pre></div>
|
|
61
|
+
</section>
|
|
62
|
+
</main>
|
|
63
|
+
<script>
|
|
64
|
+
(function(){
|
|
65
|
+
// Per-run write token, injected same-origin at GET /. A cross-origin page can
|
|
66
|
+
// neither read it nor (CORS-closed) reach /api — so only this page can POST.
|
|
67
|
+
var TOKEN="__CANEVAS_TOKEN__";
|
|
68
|
+
var decisions=[],idx=0,paneTimer=null;
|
|
69
|
+
function esc(s){return String(s==null?"":s).replace(/[&<>]/g,function(c){return{"&":"&","<":"<",">":">"}[c]})}
|
|
70
|
+
function setStatus(msg,ok){var s=document.getElementById("answerStatus");s.textContent=msg;s.className="answerStatus"+(msg?(ok?" ok":" err"):"")}
|
|
71
|
+
async function answer(decisionId,answerId,btn){
|
|
72
|
+
setStatus("Envoi de la réponse…",true);
|
|
73
|
+
if(btn){btn.disabled=true}
|
|
74
|
+
try{
|
|
75
|
+
var r=await fetch("/api/decisions/"+encodeURIComponent(decisionId)+"/answer",{method:"POST",headers:{"content-type":"application/json","x-canevas-token":TOKEN},body:JSON.stringify({answerId:answerId})});
|
|
76
|
+
var j={};try{j=await r.json()}catch(e){}
|
|
77
|
+
if(r.ok){setStatus("✓ Réponse « "+answerId+" » transmise à l'agent.",true)}
|
|
78
|
+
else{setStatus("✗ "+(j&&j.error?j.error:("échec ("+r.status+")")),false)}
|
|
79
|
+
}catch(e){setStatus("✗ erreur réseau — réessayez.",false)}
|
|
80
|
+
load();
|
|
81
|
+
}
|
|
82
|
+
async function load(){
|
|
83
|
+
try{var r=await fetch("/api/decisions");var j=await r.json();decisions=(j.decisions||[])}catch(e){decisions=[]}
|
|
84
|
+
if(idx>=decisions.length)idx=Math.max(0,decisions.length-1);
|
|
85
|
+
render();
|
|
86
|
+
}
|
|
87
|
+
function render(){
|
|
88
|
+
document.getElementById("count").textContent=decisions.length+" décision"+(decisions.length>1?"s":"")+" en attente";
|
|
89
|
+
var card=document.getElementById("card");
|
|
90
|
+
if(!decisions.length){card.innerHTML='<div class="empty">Aucune décision en attente 🎉</div>';document.getElementById("pos").textContent="";setPane(null);return}
|
|
91
|
+
var d=decisions[idx];
|
|
92
|
+
var opts=(d.options&&d.options.length)?d.options:[{id:"go",label:"Valider"},{id:"veto",label:"Refuser"},{id:"defer",label:"Différer"}];
|
|
93
|
+
card.innerHTML=
|
|
94
|
+
'<span class="chan '+esc(d.channel||"")+'">'+esc(d.channel||"décision")+'</span>'+
|
|
95
|
+
'<div class="q">'+esc(d.question)+'</div>'+
|
|
96
|
+
'<div class="meta"><span>agent: '+esc(d.instance)+'</span>'+(d.workspace?'<span>ws: '+esc(d.workspace)+'</span>':'')+(d.createdAt?'<span>'+esc(d.createdAt)+'</span>':'')+'</div>'+
|
|
97
|
+
'<div class="answers">'+opts.map(function(o){return '<button class="ans" data-a="'+esc(o.id)+'">'+esc(o.label)+'</button>'}).join("")+'</div>';
|
|
98
|
+
Array.prototype.forEach.call(card.querySelectorAll(".ans"),function(btn){
|
|
99
|
+
btn.onclick=function(){answer(d.id,btn.getAttribute("data-a"),btn)};
|
|
100
|
+
});
|
|
101
|
+
document.getElementById("pos").textContent=(idx+1)+" / "+decisions.length;
|
|
102
|
+
setPane(d.sessionRef&&d.sessionRef.tmuxName?d.sessionRef.tmuxName:null);
|
|
103
|
+
}
|
|
104
|
+
function setPane(tmuxName){
|
|
105
|
+
if(paneTimer){clearInterval(paneTimer);paneTimer=null}
|
|
106
|
+
var pre=document.getElementById("pane"),label=document.getElementById("paneLabel");
|
|
107
|
+
if(!tmuxName){pre.textContent="— (pas de session tmux liée)";label.textContent="Session CLI";return}
|
|
108
|
+
label.textContent="Session CLI · "+tmuxName;
|
|
109
|
+
async function poll(){
|
|
110
|
+
try{var r=await fetch("/api/sessions/"+encodeURIComponent(tmuxName)+"/pane?lines=200");var j=await r.json();
|
|
111
|
+
pre.textContent=j.degraded?"(runtime indisponible — capturePane degraded)":(j.text||"(pane vide)")}catch(e){pre.textContent="(erreur de lecture du pane)"}
|
|
112
|
+
}
|
|
113
|
+
poll();paneTimer=setInterval(poll,2000);
|
|
114
|
+
}
|
|
115
|
+
function go(delta){if(!decisions.length)return;idx=(idx+delta+decisions.length)%decisions.length;render()}
|
|
116
|
+
document.getElementById("prev").onclick=function(){go(-1)};
|
|
117
|
+
document.getElementById("next").onclick=function(){go(1)};
|
|
118
|
+
document.addEventListener("keydown",function(e){if(e.key==="ArrowLeft")go(-1);else if(e.key==="ArrowRight")go(1)});
|
|
119
|
+
load();setInterval(load,5000);
|
|
120
|
+
})();
|
|
121
|
+
</script>
|
|
122
|
+
</body>
|
|
123
|
+
</html>`;
|
|
124
|
+
//# sourceMappingURL=ui.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui.js","sourceRoot":"","sources":["../../../src/runtime/canevas/ui.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,+EAA+E;AAC/E,kFAAkF;AAClF,iFAAiF;AACjF,mEAAmE;AAEnE,MAAM,CAAC,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAqHpB,CAAC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { H2ALaunchContext } from "../../../session.js";
|
|
2
|
+
import { type H2AObjectiveLoop } from "../index.js";
|
|
3
|
+
import { type AgentsSnapshot, type InboxSnapshot, type PendingDecision, type RefsRollup, type RefStatus } from "./decision.js";
|
|
4
|
+
import type { ActionSink } from "./execute.js";
|
|
5
|
+
export declare function readAgents(): Promise<AgentsSnapshot>;
|
|
6
|
+
/** Shape of a `track query --format json` row we consume. */
|
|
7
|
+
interface TrackQueryRow {
|
|
8
|
+
readonly id?: string;
|
|
9
|
+
readonly bucket?: string;
|
|
10
|
+
readonly realization?: string;
|
|
11
|
+
readonly acceptance?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* PURE mapping: a track item's (bucket, realization, acceptance) → loop RefStatus.
|
|
15
|
+
* Buckets: AWAITED | DROPPED | DONE | TO-DO. Conservative + unit-tested so the
|
|
16
|
+
* semantics stay explicit and easy to tune. Unknown/missing → "unknown" (the
|
|
17
|
+
* core treats that as NOT satisfied → never auto-closes on it).
|
|
18
|
+
*/
|
|
19
|
+
export declare function mapTrackAggregateToRefStatus(item: TrackQueryRow | undefined): RefStatus;
|
|
20
|
+
export declare function readRefsRollup(loop: H2AObjectiveLoop): RefsRollup;
|
|
21
|
+
/** PURE: escalations in an inbox → pending decisions for `agentId`. */
|
|
22
|
+
export declare function pendingDecisionsFromInbox(envelopes: readonly {
|
|
23
|
+
readonly id: string;
|
|
24
|
+
readonly type: string;
|
|
25
|
+
}[], agentId: string): PendingDecision[];
|
|
26
|
+
export declare function readInbox(loop: H2AObjectiveLoop, root: string): InboxSnapshot;
|
|
27
|
+
export type WakePlan = {
|
|
28
|
+
readonly kind: "wake";
|
|
29
|
+
readonly instance: string;
|
|
30
|
+
readonly host?: string;
|
|
31
|
+
readonly launchContext: H2ALaunchContext;
|
|
32
|
+
readonly instructionLine: string;
|
|
33
|
+
} | {
|
|
34
|
+
readonly kind: "skip";
|
|
35
|
+
readonly reason: string;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* PURE: given the loop, a target agent id, the FRESH sessions (already
|
|
39
|
+
* expiry-filtered by listPresence) and prior wake timestamps, decide whether to
|
|
40
|
+
* wake (and with what launchContext/line) or skip (and why). No IO. Freshness +
|
|
41
|
+
* cooldown are the safety gates BEFORE the driver's own human-typing guard.
|
|
42
|
+
*/
|
|
43
|
+
export declare function planWakeTarget(input: {
|
|
44
|
+
readonly loop: H2AObjectiveLoop;
|
|
45
|
+
readonly agentId: string;
|
|
46
|
+
readonly freshSessions: readonly {
|
|
47
|
+
readonly instance: string;
|
|
48
|
+
readonly launchContext?: H2ALaunchContext;
|
|
49
|
+
}[];
|
|
50
|
+
readonly priorWakeAtByAgent: ReadonlyMap<string, number>;
|
|
51
|
+
readonly now: number;
|
|
52
|
+
}): WakePlan;
|
|
53
|
+
/** PURE: latest applied-wake epoch ms per agent, from the loop event journal. */
|
|
54
|
+
export declare function priorWakeAtByAgent(events: readonly {
|
|
55
|
+
readonly type: string;
|
|
56
|
+
readonly at: string;
|
|
57
|
+
readonly payload?: unknown;
|
|
58
|
+
}[]): Map<string, number>;
|
|
59
|
+
export type LaunchPlan = {
|
|
60
|
+
readonly kind: "emit";
|
|
61
|
+
readonly host?: string;
|
|
62
|
+
readonly reason: string;
|
|
63
|
+
} | {
|
|
64
|
+
readonly kind: "skip";
|
|
65
|
+
readonly reason: string;
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* PURE: decide whether to ASK for a launch of a missing enrolled agent. Bounded
|
|
69
|
+
* by `policy.maxRelaunches` (stop asking after N) + a per-agent cooldown. h2a
|
|
70
|
+
* ASKS (records in the loop journal / — later — an envelope to a remote); it
|
|
71
|
+
* NEVER spawns arbitrary agents itself (spec objective-loop §Non-goals).
|
|
72
|
+
*/
|
|
73
|
+
export declare function planLaunchTarget(input: {
|
|
74
|
+
readonly loop: H2AObjectiveLoop;
|
|
75
|
+
readonly agentId: string;
|
|
76
|
+
readonly priorCount: number;
|
|
77
|
+
readonly priorLatestAt?: number;
|
|
78
|
+
readonly now: number;
|
|
79
|
+
}): LaunchPlan;
|
|
80
|
+
/** PURE: prior launch-request count + latest epoch ms per agent, from the journal. */
|
|
81
|
+
export declare function priorLaunchByAgent(events: readonly {
|
|
82
|
+
readonly type: string;
|
|
83
|
+
readonly at: string;
|
|
84
|
+
readonly payload?: unknown;
|
|
85
|
+
}[]): Map<string, {
|
|
86
|
+
count: number;
|
|
87
|
+
latestAt?: number;
|
|
88
|
+
}>;
|
|
89
|
+
export declare function buildActionSink(): ActionSink;
|
|
90
|
+
export {};
|
|
91
|
+
//# sourceMappingURL=adapters.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapters.d.ts","sourceRoot":"","sources":["../../../../src/runtime/loop/engine/adapters.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
// Objective-loop tick — IO ADAPTERS (imperative shell).
|
|
2
|
+
//
|
|
3
|
+
// This is the ONLY loop-engine file allowed to reach `@sentropic/h2a-runtime`
|
|
4
|
+
// (lazy import). `decision.ts` and `tick.ts` stay runtime-free; the golden-rule
|
|
5
|
+
// scan in `remote-facade.test.js` enforces it. Consuming the TYPED projection
|
|
6
|
+
// (not parsed CLI stdout) is a double-consensus ruling (2026-07-02).
|
|
7
|
+
import { spawnSync } from "node:child_process";
|
|
8
|
+
import { localTmuxDriver } from "../../drive/index.js";
|
|
9
|
+
import { createLocalStore } from "../../local-files/store.js";
|
|
10
|
+
import { listPresence } from "../../local-files/presence.js";
|
|
11
|
+
import { listLoopEvents, readObjectiveLoop, updateObjectiveLoopStatus } from "../index.js";
|
|
12
|
+
import { loopRefLocator } from "./decision.js";
|
|
13
|
+
// --- Agents adapter (lazy runtime; degraded-clean on absence/failure) ---------
|
|
14
|
+
export async function readAgents() {
|
|
15
|
+
// Variable-typed specifier defeats tsc static resolution: `@sentropic/h2a`
|
|
16
|
+
// must NEVER hard-depend on the heavy runtime (same trick as bin.ts).
|
|
17
|
+
const RUNTIME_PKG = "@sentropic/h2a-runtime";
|
|
18
|
+
try {
|
|
19
|
+
const rt = (await import(RUNTIME_PKG));
|
|
20
|
+
if (typeof rt.projectAgentsForH2a !== "function") {
|
|
21
|
+
return { degraded: true, agents: [] };
|
|
22
|
+
}
|
|
23
|
+
const env = rt.projectAgentsForH2a();
|
|
24
|
+
const agents = Array.isArray(env.agents) ? env.agents : [];
|
|
25
|
+
return {
|
|
26
|
+
degraded: false,
|
|
27
|
+
...(typeof env.version === "number" ? { version: env.version } : {}),
|
|
28
|
+
agents,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
// Runtime not installed or failed → degraded; NEVER invent agent state.
|
|
33
|
+
return { degraded: true, agents: [] };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* PURE mapping: a track item's (bucket, realization, acceptance) → loop RefStatus.
|
|
38
|
+
* Buckets: AWAITED | DROPPED | DONE | TO-DO. Conservative + unit-tested so the
|
|
39
|
+
* semantics stay explicit and easy to tune. Unknown/missing → "unknown" (the
|
|
40
|
+
* core treats that as NOT satisfied → never auto-closes on it).
|
|
41
|
+
*/
|
|
42
|
+
export function mapTrackAggregateToRefStatus(item) {
|
|
43
|
+
if (!item)
|
|
44
|
+
return "unknown";
|
|
45
|
+
const bucket = (item.bucket ?? "").toUpperCase();
|
|
46
|
+
const acceptance = (item.acceptance ?? "").toLowerCase();
|
|
47
|
+
const realization = (item.realization ?? "").toLowerCase();
|
|
48
|
+
if (bucket === "DONE") {
|
|
49
|
+
return acceptance === "accepted" || acceptance === "pass" ? "accepted" : "done";
|
|
50
|
+
}
|
|
51
|
+
if (bucket === "DROPPED" || realization === "cancelled")
|
|
52
|
+
return "rejected";
|
|
53
|
+
if (bucket === "AWAITED" || bucket === "TO-DO")
|
|
54
|
+
return "pending";
|
|
55
|
+
return "unknown";
|
|
56
|
+
}
|
|
57
|
+
export function readRefsRollup(loop) {
|
|
58
|
+
// No refs declared → nothing to resolve; not degraded (no track call).
|
|
59
|
+
if (loop.refs.length === 0)
|
|
60
|
+
return { degraded: false, refs: [] };
|
|
61
|
+
const cwds = loop.repos.map((r) => r.path).filter((p) => !!p);
|
|
62
|
+
if (cwds.length === 0)
|
|
63
|
+
cwds.push(process.cwd());
|
|
64
|
+
const itemsById = new Map();
|
|
65
|
+
let anyQueryOk = false;
|
|
66
|
+
for (const cwd of cwds) {
|
|
67
|
+
const res = spawnSync("track", ["query", "--format", "json"], { cwd, encoding: "utf8" });
|
|
68
|
+
if (res.error || res.status !== 0 || !res.stdout)
|
|
69
|
+
continue;
|
|
70
|
+
try {
|
|
71
|
+
const rows = JSON.parse(res.stdout);
|
|
72
|
+
if (Array.isArray(rows)) {
|
|
73
|
+
anyQueryOk = true;
|
|
74
|
+
for (const row of rows) {
|
|
75
|
+
if (row && typeof row.id === "string")
|
|
76
|
+
itemsById.set(row.id, row);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// non-JSON output → ignore this repo
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// `track` unreachable in every repo → degraded (do not invent ref state).
|
|
85
|
+
if (!anyQueryOk)
|
|
86
|
+
return { degraded: true, refs: [] };
|
|
87
|
+
return {
|
|
88
|
+
degraded: false,
|
|
89
|
+
refs: loop.refs.map((r) => ({
|
|
90
|
+
locator: loopRefLocator(r),
|
|
91
|
+
status: mapTrackAggregateToRefStatus(itemsById.get(r.aggregateId)),
|
|
92
|
+
})),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
// --- Inbox adapter (READ-ONLY) ------------------------------------------------
|
|
96
|
+
// The h2a bus is a negotiation protocol (register/propose/accept/reject/counter/
|
|
97
|
+
// withdraw/event/escalate). The clearest "needs a human/agent decision" signal is
|
|
98
|
+
// an `escalate` envelope sitting in an enrolled agent's inbox. We surface those as
|
|
99
|
+
// pending decisions so the loop reports `waiting-human` and routes them.
|
|
100
|
+
// TODO(next): also correlate unresolved `propose` (no matching accept/reject).
|
|
101
|
+
/** PURE: escalations in an inbox → pending decisions for `agentId`. */
|
|
102
|
+
export function pendingDecisionsFromInbox(envelopes, agentId) {
|
|
103
|
+
return envelopes
|
|
104
|
+
.filter((e) => e.type === "escalate")
|
|
105
|
+
.map((e) => ({ id: e.id, forAgent: agentId }));
|
|
106
|
+
}
|
|
107
|
+
export function readInbox(loop, root) {
|
|
108
|
+
const enrolled = loop.agents
|
|
109
|
+
.map((a) => ({ agentId: a.id, instance: a.h2aInstance }))
|
|
110
|
+
.filter((x) => typeof x.instance === "string");
|
|
111
|
+
if (enrolled.length === 0)
|
|
112
|
+
return { pendingDecisions: [] };
|
|
113
|
+
let store;
|
|
114
|
+
try {
|
|
115
|
+
store = createLocalStore({ root });
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
return { pendingDecisions: [] };
|
|
119
|
+
}
|
|
120
|
+
const pending = [];
|
|
121
|
+
const seen = new Set();
|
|
122
|
+
for (const { agentId, instance } of enrolled) {
|
|
123
|
+
let envelopes;
|
|
124
|
+
try {
|
|
125
|
+
envelopes = store.readInbox(instance);
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
for (const d of pendingDecisionsFromInbox(envelopes, agentId)) {
|
|
131
|
+
if (!seen.has(d.id)) {
|
|
132
|
+
seen.add(d.id);
|
|
133
|
+
pending.push(d);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return { pendingDecisions: pending };
|
|
138
|
+
}
|
|
139
|
+
const WAKE_COOLDOWN_FLOOR_MS = 300_000; // 5 min
|
|
140
|
+
/**
|
|
141
|
+
* PURE: given the loop, a target agent id, the FRESH sessions (already
|
|
142
|
+
* expiry-filtered by listPresence) and prior wake timestamps, decide whether to
|
|
143
|
+
* wake (and with what launchContext/line) or skip (and why). No IO. Freshness +
|
|
144
|
+
* cooldown are the safety gates BEFORE the driver's own human-typing guard.
|
|
145
|
+
*/
|
|
146
|
+
export function planWakeTarget(input) {
|
|
147
|
+
const agent = input.loop.agents.find((a) => a.id === input.agentId);
|
|
148
|
+
if (!agent || agent.h2aInstance === undefined)
|
|
149
|
+
return { kind: "skip", reason: "no-h2a-instance" };
|
|
150
|
+
const cooldownMs = Math.max(input.loop.policy.tickMs, WAKE_COOLDOWN_FLOOR_MS);
|
|
151
|
+
const last = input.priorWakeAtByAgent.get(input.agentId);
|
|
152
|
+
if (last !== undefined && input.now - last < cooldownMs)
|
|
153
|
+
return { kind: "skip", reason: "cooldown" };
|
|
154
|
+
const session = input.freshSessions.find((s) => s.instance === agent.h2aInstance);
|
|
155
|
+
if (!session || !session.launchContext || !session.launchContext.tmux) {
|
|
156
|
+
return { kind: "skip", reason: "no-fresh-tmux-session" };
|
|
157
|
+
}
|
|
158
|
+
const instructionLine = `[h2a-wake reason=loop loopId=${input.loop.id} at=${new Date(input.now).toISOString()}] ` +
|
|
159
|
+
`reprends l'objectif: ${input.loop.goal}`;
|
|
160
|
+
return {
|
|
161
|
+
kind: "wake",
|
|
162
|
+
instance: agent.h2aInstance,
|
|
163
|
+
...(agent.host !== undefined ? { host: agent.host } : {}),
|
|
164
|
+
launchContext: session.launchContext,
|
|
165
|
+
instructionLine
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
/** PURE: latest applied-wake epoch ms per agent, from the loop event journal. */
|
|
169
|
+
export function priorWakeAtByAgent(events) {
|
|
170
|
+
const out = new Map();
|
|
171
|
+
for (const e of events) {
|
|
172
|
+
if (e.type !== "loop.action.applied")
|
|
173
|
+
continue;
|
|
174
|
+
const p = e.payload;
|
|
175
|
+
if (!p || p.action !== "wake" || typeof p.key !== "string" || !p.key.startsWith("wake:"))
|
|
176
|
+
continue;
|
|
177
|
+
const agentId = p.key.slice("wake:".length);
|
|
178
|
+
const at = Date.parse(e.at);
|
|
179
|
+
if (Number.isNaN(at))
|
|
180
|
+
continue;
|
|
181
|
+
const prev = out.get(agentId);
|
|
182
|
+
if (prev === undefined || at > prev)
|
|
183
|
+
out.set(agentId, at);
|
|
184
|
+
}
|
|
185
|
+
return out;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* PURE: decide whether to ASK for a launch of a missing enrolled agent. Bounded
|
|
189
|
+
* by `policy.maxRelaunches` (stop asking after N) + a per-agent cooldown. h2a
|
|
190
|
+
* ASKS (records in the loop journal / — later — an envelope to a remote); it
|
|
191
|
+
* NEVER spawns arbitrary agents itself (spec objective-loop §Non-goals).
|
|
192
|
+
*/
|
|
193
|
+
export function planLaunchTarget(input) {
|
|
194
|
+
const agent = input.loop.agents.find((a) => a.id === input.agentId);
|
|
195
|
+
if (!agent)
|
|
196
|
+
return { kind: "skip", reason: "unknown-agent" };
|
|
197
|
+
if (input.priorCount >= input.loop.policy.maxRelaunches)
|
|
198
|
+
return { kind: "skip", reason: "max-relaunches" };
|
|
199
|
+
const cooldownMs = Math.max(input.loop.policy.tickMs, WAKE_COOLDOWN_FLOOR_MS);
|
|
200
|
+
if (input.priorLatestAt !== undefined && input.now - input.priorLatestAt < cooldownMs) {
|
|
201
|
+
return { kind: "skip", reason: "cooldown" };
|
|
202
|
+
}
|
|
203
|
+
return {
|
|
204
|
+
kind: "emit",
|
|
205
|
+
...(agent.host !== undefined ? { host: agent.host } : {}),
|
|
206
|
+
reason: "enrolled agent missing while work pending"
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
/** PURE: prior launch-request count + latest epoch ms per agent, from the journal. */
|
|
210
|
+
export function priorLaunchByAgent(events) {
|
|
211
|
+
const out = new Map();
|
|
212
|
+
for (const e of events) {
|
|
213
|
+
if (e.type !== "loop.action.applied")
|
|
214
|
+
continue;
|
|
215
|
+
const p = e.payload;
|
|
216
|
+
if (!p || p.action !== "request-launch" || typeof p.key !== "string" || !p.key.startsWith("request-launch:")) {
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
const agentId = p.key.slice("request-launch:".length);
|
|
220
|
+
const at = Date.parse(e.at);
|
|
221
|
+
const cur = out.get(agentId) ?? { count: 0 };
|
|
222
|
+
cur.count += 1;
|
|
223
|
+
if (!Number.isNaN(at) && (cur.latestAt === undefined || at > cur.latestAt))
|
|
224
|
+
cur.latestAt = at;
|
|
225
|
+
out.set(agentId, cur);
|
|
226
|
+
}
|
|
227
|
+
return out;
|
|
228
|
+
}
|
|
229
|
+
// --- Action sink (effects for `--execute`) ------------------------------------
|
|
230
|
+
// `close` = idempotent store status flip (ZERO injection). `wake` = fresh-session
|
|
231
|
+
// + cooldown gated localTmuxDriver (which re-checks the human-typing guard at the
|
|
232
|
+
// last moment; defer → "deferred", stays pending). request-launch / route-decision
|
|
233
|
+
// still `skipped` (their guarded slices land next). NEVER chain/headless/auto — a
|
|
234
|
+
// defer must not fall back to a headless injection (double-consensus RISK #1).
|
|
235
|
+
export function buildActionSink() {
|
|
236
|
+
return {
|
|
237
|
+
async close(_action, ctx) {
|
|
238
|
+
const { changed } = updateObjectiveLoopStatus(ctx.root, ctx.loopId, "done", {
|
|
239
|
+
now: ctx.now,
|
|
240
|
+
reason: "objective-loop tick: all primary/target refs satisfied"
|
|
241
|
+
});
|
|
242
|
+
return changed ? "done" : "skipped";
|
|
243
|
+
},
|
|
244
|
+
async requestLaunch(action, ctx) {
|
|
245
|
+
const loop = readObjectiveLoop(ctx.root, ctx.loopId);
|
|
246
|
+
const prior = priorLaunchByAgent(listLoopEvents(ctx.root, ctx.loopId)).get(action.agentId ?? "") ?? { count: 0 };
|
|
247
|
+
const plan = planLaunchTarget({
|
|
248
|
+
loop,
|
|
249
|
+
agentId: action.agentId ?? "",
|
|
250
|
+
priorCount: prior.count,
|
|
251
|
+
...(prior.latestAt !== undefined ? { priorLatestAt: prior.latestAt } : {}),
|
|
252
|
+
now: ctx.now
|
|
253
|
+
});
|
|
254
|
+
// ASK, never spawn (spec §Non-goals: "h2a asks remote/runtime to spawn").
|
|
255
|
+
// MVP: record the request in the loop journal (loop.action.applied
|
|
256
|
+
// {request-launch}) — a conductor/remote consumes it. TODO: also emit a
|
|
257
|
+
// conductor-launch-request envelope to a live remote (createEnvelope +
|
|
258
|
+
// putInboxMessage) once actor-instance + workspace resolution is settled.
|
|
259
|
+
return plan.kind === "emit" ? "done" : "skipped";
|
|
260
|
+
},
|
|
261
|
+
async wake(action, ctx) {
|
|
262
|
+
const loop = readObjectiveLoop(ctx.root, ctx.loopId);
|
|
263
|
+
const freshSessions = listPresence(ctx.root, { now: ctx.now }).map((s) => ({
|
|
264
|
+
instance: s.instance,
|
|
265
|
+
...(s.launchContext !== undefined ? { launchContext: s.launchContext } : {})
|
|
266
|
+
}));
|
|
267
|
+
const plan = planWakeTarget({
|
|
268
|
+
loop,
|
|
269
|
+
agentId: action.agentId ?? "",
|
|
270
|
+
freshSessions,
|
|
271
|
+
priorWakeAtByAgent: priorWakeAtByAgent(listLoopEvents(ctx.root, ctx.loopId)),
|
|
272
|
+
now: ctx.now
|
|
273
|
+
});
|
|
274
|
+
if (plan.kind === "skip")
|
|
275
|
+
return "skipped";
|
|
276
|
+
// localTmuxDriver ONLY — never chain/headless/auto (RISK #1). It applies the
|
|
277
|
+
// human-typing guard at the last moment and DEFERS (false) if a human is
|
|
278
|
+
// active in the pane; a defer stays pending and re-fires next tick.
|
|
279
|
+
const ok = await localTmuxDriver().drive({
|
|
280
|
+
to: plan.instance,
|
|
281
|
+
...(plan.host !== undefined ? { host: plan.host } : {}),
|
|
282
|
+
instructionLine: plan.instructionLine,
|
|
283
|
+
launchContext: plan.launchContext
|
|
284
|
+
});
|
|
285
|
+
return ok ? "done" : "deferred";
|
|
286
|
+
},
|
|
287
|
+
async routeDecision() {
|
|
288
|
+
return "skipped";
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
//# sourceMappingURL=adapters.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapters.js","sourceRoot":"","sources":["../../../../src/runtime/loop/engine/adapters.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { H2AObjectiveLoop, H2ALoopTrackRef } from "../index.js";
|
|
2
|
+
export interface ProjectedAgent {
|
|
3
|
+
readonly id: string;
|
|
4
|
+
readonly tool: string;
|
|
5
|
+
readonly state: string;
|
|
6
|
+
readonly cwd: string;
|
|
7
|
+
readonly tmuxSession?: string;
|
|
8
|
+
readonly jobId?: string;
|
|
9
|
+
readonly h2aInstance?: string;
|
|
10
|
+
readonly capabilities: {
|
|
11
|
+
readonly attach: boolean;
|
|
12
|
+
readonly logs: boolean;
|
|
13
|
+
readonly remote: boolean;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export interface AgentsSnapshot {
|
|
17
|
+
readonly degraded: boolean;
|
|
18
|
+
readonly version?: number;
|
|
19
|
+
readonly agents: readonly ProjectedAgent[];
|
|
20
|
+
}
|
|
21
|
+
export type RefStatus = "accepted" | "done" | "pending" | "rejected" | "blocked" | "stale" | "unknown";
|
|
22
|
+
export interface RolledRef {
|
|
23
|
+
readonly locator: string;
|
|
24
|
+
readonly status: RefStatus;
|
|
25
|
+
}
|
|
26
|
+
export interface RefsRollup {
|
|
27
|
+
readonly degraded: boolean;
|
|
28
|
+
readonly refs: readonly RolledRef[];
|
|
29
|
+
}
|
|
30
|
+
export interface PendingDecision {
|
|
31
|
+
readonly id: string;
|
|
32
|
+
readonly forAgent?: string;
|
|
33
|
+
}
|
|
34
|
+
export interface InboxSnapshot {
|
|
35
|
+
readonly pendingDecisions: readonly PendingDecision[];
|
|
36
|
+
}
|
|
37
|
+
export interface TickInput {
|
|
38
|
+
readonly loop: H2AObjectiveLoop;
|
|
39
|
+
readonly agents: AgentsSnapshot;
|
|
40
|
+
readonly refs: RefsRollup;
|
|
41
|
+
readonly inbox: InboxSnapshot;
|
|
42
|
+
readonly now: number;
|
|
43
|
+
}
|
|
44
|
+
export type TickActionType = "request-launch" | "wake" | "route-decision" | "close" | "noop";
|
|
45
|
+
export interface TickAction {
|
|
46
|
+
readonly type: TickActionType;
|
|
47
|
+
readonly reason: string;
|
|
48
|
+
readonly agentId?: string;
|
|
49
|
+
readonly refLocator?: string;
|
|
50
|
+
readonly decisionId?: string;
|
|
51
|
+
}
|
|
52
|
+
export type TickOutcome = "running" | "waiting-human" | "waiting-agent" | "stalled" | "degraded" | "eligible-for-close" | "failed";
|
|
53
|
+
export interface TickPlan {
|
|
54
|
+
readonly loopId: string;
|
|
55
|
+
readonly degraded: boolean;
|
|
56
|
+
readonly outcome: TickOutcome;
|
|
57
|
+
readonly close: boolean;
|
|
58
|
+
readonly actions: readonly TickAction[];
|
|
59
|
+
readonly reasons: readonly string[];
|
|
60
|
+
}
|
|
61
|
+
/** Stable join key for a declared loop track-ref. Adapters MUST use the same. */
|
|
62
|
+
export declare function loopRefLocator(ref: H2ALoopTrackRef): string;
|
|
63
|
+
/**
|
|
64
|
+
* Pure objective-loop tick decision. Deterministic; no IO. Implements the spec
|
|
65
|
+
* (2026-06-26 §Relaunch + §C completion) at MVP scope: degraded short-circuit,
|
|
66
|
+
* multi-ref completion rollup, and conservative launch/wake/route intents.
|
|
67
|
+
*
|
|
68
|
+
* The shell never auto-closes unless `close===true` (policy.closeWhenRefsSatisfied).
|
|
69
|
+
*/
|
|
70
|
+
export declare function planLoopTick(input: TickInput): TickPlan;
|
|
71
|
+
//# sourceMappingURL=decision.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decision.d.ts","sourceRoot":"","sources":["../../../../src/runtime/loop/engine/decision.ts"],"names":[],"mappings":"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"}
|