loki-mode 6.36.1 → 6.36.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.
- package/README.md +2 -2
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/dashboard/__init__.py +1 -1
- package/dashboard/server.py +107 -2
- package/dashboard/static/index.html +1 -1
- package/docs/INSTALLATION.md +1 -1
- package/mcp/__init__.py +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,11 +10,11 @@
|
|
|
10
10
|
[](https://www.autonomi.dev/)
|
|
11
11
|
[](https://hub.docker.com/r/asklokesh/loki-mode)
|
|
12
12
|
|
|
13
|
-
**Current Version: v6.
|
|
13
|
+
**Current Version: v6.36.1**
|
|
14
14
|
|
|
15
15
|
### Traction
|
|
16
16
|
|
|
17
|
-
**
|
|
17
|
+
**741 stars** | **149 forks** | **11,400+ Docker pulls** | **477 npm downloads (last 7d)** | **606 commits** | **263 releases published** | **20+ releases in 72 hours (March 17-19, 2026)**
|
|
18
18
|
|
|
19
19
|
---
|
|
20
20
|
|
package/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: loki-mode
|
|
|
3
3
|
description: Multi-agent autonomous startup system. Triggers on "Loki Mode". Takes PRD to deployed product with minimal human intervention. Requires --dangerously-skip-permissions flag.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Loki Mode v6.36.
|
|
6
|
+
# Loki Mode v6.36.2
|
|
7
7
|
|
|
8
8
|
**You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
|
|
9
9
|
|
|
@@ -267,4 +267,4 @@ The following features are documented in skill modules but not yet fully automat
|
|
|
267
267
|
| Quality gates 3-reviewer system | Implemented (v5.35.0) | 5 specialist reviewers in `skills/quality-gates.md`; execution in run.sh |
|
|
268
268
|
| Benchmarks (HumanEval, SWE-bench) | Infrastructure only | Runner scripts and datasets exist in `benchmarks/`; no published results |
|
|
269
269
|
|
|
270
|
-
**v6.36.
|
|
270
|
+
**v6.36.2 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
6.36.
|
|
1
|
+
6.36.2
|
package/dashboard/__init__.py
CHANGED
package/dashboard/server.py
CHANGED
|
@@ -289,14 +289,98 @@ start_time = datetime.now(timezone.utc)
|
|
|
289
289
|
_dashboard_start_time = time.time()
|
|
290
290
|
|
|
291
291
|
|
|
292
|
+
async def _push_loki_state_loop() -> None:
|
|
293
|
+
"""Background loop: push .loki/ state changes to all WebSocket clients.
|
|
294
|
+
|
|
295
|
+
Reads dashboard-state.json every 2s when running (30s when idle) and
|
|
296
|
+
broadcasts a state_update message so clients don't rely solely on polling.
|
|
297
|
+
"""
|
|
298
|
+
last_mtime: float = 0.0
|
|
299
|
+
while True:
|
|
300
|
+
try:
|
|
301
|
+
if not manager.active_connections:
|
|
302
|
+
await asyncio.sleep(5)
|
|
303
|
+
continue
|
|
304
|
+
|
|
305
|
+
loki_dir = _get_loki_dir()
|
|
306
|
+
state_file = loki_dir / "dashboard-state.json"
|
|
307
|
+
|
|
308
|
+
if state_file.exists():
|
|
309
|
+
try:
|
|
310
|
+
mtime = state_file.stat().st_mtime
|
|
311
|
+
except OSError:
|
|
312
|
+
mtime = 0.0
|
|
313
|
+
|
|
314
|
+
# Only broadcast if file changed
|
|
315
|
+
if mtime != last_mtime:
|
|
316
|
+
last_mtime = mtime
|
|
317
|
+
try:
|
|
318
|
+
raw = json.loads(state_file.read_text())
|
|
319
|
+
# Transform to StatusResponse-compatible format
|
|
320
|
+
agents_list = raw.get("agents", [])
|
|
321
|
+
running_agents = len(agents_list) if isinstance(agents_list, list) else 0
|
|
322
|
+
tasks = raw.get("tasks", {})
|
|
323
|
+
pending = tasks.get("pending", [])
|
|
324
|
+
in_prog = tasks.get("inProgress", [])
|
|
325
|
+
status_str = raw.get("mode", "autonomous")
|
|
326
|
+
if status_str == "paused":
|
|
327
|
+
status_str = "paused"
|
|
328
|
+
elif status_str in ("stopped", ""):
|
|
329
|
+
status_str = "stopped"
|
|
330
|
+
else:
|
|
331
|
+
status_str = "running"
|
|
332
|
+
|
|
333
|
+
payload = {
|
|
334
|
+
"status": status_str,
|
|
335
|
+
"phase": raw.get("phase", ""),
|
|
336
|
+
"iteration": raw.get("iteration", 0),
|
|
337
|
+
"complexity": raw.get("complexity", "standard"),
|
|
338
|
+
"mode": raw.get("mode", ""),
|
|
339
|
+
"provider": raw.get("provider", "claude"),
|
|
340
|
+
"running_agents": running_agents,
|
|
341
|
+
"pending_tasks": len(pending) if isinstance(pending, list) else 0,
|
|
342
|
+
"current_task": in_prog[0].get("payload", {}).get("action", "") if isinstance(in_prog, list) and in_prog else "",
|
|
343
|
+
"version": raw.get("version", ""),
|
|
344
|
+
}
|
|
345
|
+
await manager.broadcast({
|
|
346
|
+
"type": "status_update",
|
|
347
|
+
"data": payload,
|
|
348
|
+
})
|
|
349
|
+
except (json.JSONDecodeError, OSError, KeyError):
|
|
350
|
+
pass
|
|
351
|
+
|
|
352
|
+
# Poll faster when a session is running
|
|
353
|
+
pid_file = loki_dir / "loki.pid"
|
|
354
|
+
is_running = False
|
|
355
|
+
if pid_file.exists():
|
|
356
|
+
try:
|
|
357
|
+
pid = int(pid_file.read_text().strip())
|
|
358
|
+
os.kill(pid, 0)
|
|
359
|
+
is_running = True
|
|
360
|
+
except (ValueError, OSError, ProcessLookupError):
|
|
361
|
+
pass
|
|
362
|
+
|
|
363
|
+
await asyncio.sleep(2.0 if is_running else 30.0)
|
|
364
|
+
except asyncio.CancelledError:
|
|
365
|
+
return
|
|
366
|
+
except Exception:
|
|
367
|
+
await asyncio.sleep(5)
|
|
368
|
+
|
|
369
|
+
|
|
292
370
|
@asynccontextmanager
|
|
293
371
|
async def lifespan(app: FastAPI):
|
|
294
372
|
"""Application lifespan handler."""
|
|
295
373
|
# Startup
|
|
296
374
|
await init_db()
|
|
297
375
|
_telemetry.send_telemetry("dashboard_start")
|
|
376
|
+
push_task = asyncio.create_task(_push_loki_state_loop())
|
|
298
377
|
yield
|
|
299
378
|
# Shutdown
|
|
379
|
+
push_task.cancel()
|
|
380
|
+
try:
|
|
381
|
+
await push_task
|
|
382
|
+
except (asyncio.CancelledError, Exception):
|
|
383
|
+
pass
|
|
300
384
|
await close_db()
|
|
301
385
|
|
|
302
386
|
|
|
@@ -1573,8 +1657,29 @@ async def get_audit_summary(days: int = 7):
|
|
|
1573
1657
|
# =============================================================================
|
|
1574
1658
|
|
|
1575
1659
|
def _get_loki_dir() -> _Path:
|
|
1576
|
-
"""Get LOKI_DIR, refreshing from env on each call for consistency.
|
|
1577
|
-
|
|
1660
|
+
"""Get LOKI_DIR, refreshing from env on each call for consistency.
|
|
1661
|
+
|
|
1662
|
+
Resolution order:
|
|
1663
|
+
1. LOKI_DIR env var (set by run.sh during active sessions)
|
|
1664
|
+
2. .loki/ in current working directory
|
|
1665
|
+
3. ~/.loki/ as global fallback
|
|
1666
|
+
"""
|
|
1667
|
+
env_dir = os.environ.get("LOKI_DIR")
|
|
1668
|
+
if env_dir:
|
|
1669
|
+
return _Path(env_dir)
|
|
1670
|
+
|
|
1671
|
+
# Check CWD first
|
|
1672
|
+
cwd_loki = _Path.cwd() / ".loki"
|
|
1673
|
+
if cwd_loki.is_dir():
|
|
1674
|
+
return cwd_loki
|
|
1675
|
+
|
|
1676
|
+
# Check home directory fallback
|
|
1677
|
+
home_loki = _Path.home() / ".loki"
|
|
1678
|
+
if home_loki.is_dir():
|
|
1679
|
+
return home_loki
|
|
1680
|
+
|
|
1681
|
+
# Default: relative .loki/ (will be created when session starts)
|
|
1682
|
+
return _Path(".loki")
|
|
1578
1683
|
|
|
1579
1684
|
|
|
1580
1685
|
_SAFE_ID_RE = re.compile(r'^[a-zA-Z0-9_-]+$')
|
|
@@ -1237,7 +1237,7 @@ var LokiDashboard=(()=>{var pt=Object.defineProperty;var Pt=Object.getOwnPropert
|
|
|
1237
1237
|
}
|
|
1238
1238
|
|
|
1239
1239
|
${B}
|
|
1240
|
-
`}getAriaPattern(t){return gt[t]||{}}applyAriaPattern(t,e){let i=this.getAriaPattern(e);for(let[a,s]of Object.entries(i))if(a==="role")t.setAttribute("role",s);else{let r=a.replace(/([A-Z])/g,"-$1").toLowerCase();t.setAttribute(r,s)}}render(){}};var L={realtime:1e3,normal:2e3,background:5e3,offline:1e4},_t={vscode:L.normal,browser:L.realtime,cli:L.background},yt={baseUrl:typeof window<"u"?window.location.origin:"http://localhost:57374",wsUrl:typeof window<"u"?`${window.location.protocol==="https:"?"wss:":"ws:"}//${window.location.host}/ws`:"ws://localhost:57374/ws",pollInterval:2e3,timeout:1e4,retryAttempts:3,retryDelay:1e3},u={CONNECTED:"api:connected",DISCONNECTED:"api:disconnected",ERROR:"api:error",STATUS_UPDATE:"api:status-update",TASK_CREATED:"api:task-created",TASK_UPDATED:"api:task-updated",TASK_DELETED:"api:task-deleted",PROJECT_CREATED:"api:project-created",PROJECT_UPDATED:"api:project-updated",AGENT_UPDATE:"api:agent-update",LOG_MESSAGE:"api:log-message",MEMORY_UPDATE:"api:memory-update",CHECKLIST_UPDATE:"api:checklist-update"},C=class C extends EventTarget{static getInstance(t={}){let e=t.baseUrl||yt.baseUrl;return C._instances.has(e)||C._instances.set(e,new C(t)),C._instances.get(e)}static clearInstances(){C._instances.forEach(t=>t.disconnect()),C._instances.clear()}constructor(t={}){super(),this.config={...yt,...t},this._ws=null,this._connected=!1,this._pollInterval=null,this._reconnectTimeout=null,this._reconnectAttempts=0,this._maxReconnectAttempts=20,this._cache=new Map,this._cacheTimeout=5e3,this._vscodeApi=null,this._context=this._detectContext(),this._currentPollInterval=_t[this._context]||L.normal,this._visibilityChangeHandler=null,this._messageHandler=null,this._setupAdaptivePolling(),this._setupVSCodeBridge()}_detectContext(){return typeof acquireVsCodeApi<"u"?"vscode":typeof window<"u"&&window.location?"browser":"cli"}get context(){return this._context}static get POLL_INTERVALS(){return L}_setupAdaptivePolling(){typeof document>"u"||(this._visibilityChangeHandler=()=>{document.hidden?this._setPollInterval(L.background):this._setPollInterval(_t[this._context]||L.normal)},document.addEventListener("visibilitychange",this._visibilityChangeHandler))}_setPollInterval(t){this._currentPollInterval=t,this._pollInterval&&(this.stopPolling(),this.startPolling(null,t))}setPollMode(t){let e=L[t];e&&this._setPollInterval(e)}_setupVSCodeBridge(){if(!(typeof acquireVsCodeApi>"u")){try{this._vscodeApi=acquireVsCodeApi()}catch{console.warn("VS Code API already acquired or unavailable");return}this._messageHandler=t=>{let e=t.data;if(!(!e||!e.type))switch(e.type){case"updateStatus":this._emit(u.STATUS_UPDATE,e.data);break;case"updateTasks":this._emit(u.TASK_UPDATED,e.data);break;case"taskCreated":this._emit(u.TASK_CREATED,e.data);break;case"taskDeleted":this._emit(u.TASK_DELETED,e.data);break;case"projectCreated":this._emit(u.PROJECT_CREATED,e.data);break;case"projectUpdated":this._emit(u.PROJECT_UPDATED,e.data);break;case"agentUpdate":this._emit(u.AGENT_UPDATE,e.data);break;case"logMessage":this._emit(u.LOG_MESSAGE,e.data);break;case"memoryUpdate":this._emit(u.MEMORY_UPDATE,e.data);break;case"connected":this._connected=!0,this._emit(u.CONNECTED,e.data);break;case"disconnected":this._connected=!1,this._emit(u.DISCONNECTED,e.data);break;case"error":this._emit(u.ERROR,e.data);break;case"setPollMode":this.setPollMode(e.data.mode);break;default:this._emit(`api:${e.type}`,e.data)}},window.addEventListener("message",this._messageHandler)}}get isVSCode(){return this._context==="vscode"}postToVSCode(t,e={}){this._vscodeApi&&this._vscodeApi.postMessage({type:t,data:e})}requestRefresh(){this.postToVSCode("requestRefresh")}notifyVSCode(t,e={}){this.postToVSCode("userAction",{action:t,...e})}get baseUrl(){return this.config.baseUrl}set baseUrl(t){this.config.baseUrl=t,this.config.wsUrl=t.replace(/^http/,"ws")+"/ws"}get isConnected(){return this._connected}async connect(){if(!(this._ws&&this._ws.readyState===WebSocket.OPEN))return new Promise((t,e)=>{try{this._ws=new WebSocket(this.config.wsUrl),this._ws.onopen=()=>{this._connected=!0,this._reconnectAttempts=0,this._emit(u.CONNECTED),t()},this._ws.onclose=()=>{this._connected=!1,this._emit(u.DISCONNECTED),this._scheduleReconnect()},this._ws.onerror=i=>{this._emit(u.ERROR,{error:i}),e(i)},this._ws.onmessage=i=>{try{let a=JSON.parse(i.data);this._handleMessage(a)}catch(a){console.error("Failed to parse WebSocket message:",a)}}}catch(i){e(i)}})}disconnect(){this._ws&&(this._ws.close(),this._ws=null),this._pollInterval&&(clearInterval(this._pollInterval),this._pollInterval=null),this._reconnectTimeout&&(clearTimeout(this._reconnectTimeout),this._reconnectTimeout=null),this._connected=!1,this._cleanupGlobalListeners()}_cleanupGlobalListeners(){this._visibilityChangeHandler&&typeof document<"u"&&(document.removeEventListener("visibilitychange",this._visibilityChangeHandler),this._visibilityChangeHandler=null),this._messageHandler&&typeof window<"u"&&(window.removeEventListener("message",this._messageHandler),this._messageHandler=null)}destroy(){this.disconnect()}_scheduleReconnect(){if(this._reconnectTimeout)return;if(this._reconnectAttempts>=this._maxReconnectAttempts){console.warn("WebSocket max reconnect attempts reached, giving up"),this._emit(u.ERROR,{error:"Max reconnect attempts reached"});return}let t=Math.min(this.config.retryDelay*Math.pow(2,this._reconnectAttempts),3e4);this._reconnectAttempts++,this._reconnectTimeout=setTimeout(()=>{this._reconnectTimeout=null,this.connect().catch(()=>{})},t)}_handleMessage(t){let i={connected:u.CONNECTED,status_update:u.STATUS_UPDATE,task_created:u.TASK_CREATED,task_updated:u.TASK_UPDATED,task_deleted:u.TASK_DELETED,task_moved:u.TASK_UPDATED,project_created:u.PROJECT_CREATED,project_updated:u.PROJECT_UPDATED,agent_update:u.AGENT_UPDATE,log:u.LOG_MESSAGE}[t.type]||`api:${t.type}`;this._emit(i,t.data)}_emit(t,e={}){this.dispatchEvent(new CustomEvent(t,{detail:e}))}async _request(t,e={}){let i=`${this.config.baseUrl}${t}`,a=new AbortController,s=setTimeout(()=>a.abort(),this.config.timeout);try{let r=await fetch(i,{...e,signal:a.signal,headers:{"Content-Type":"application/json",...e.headers}});if(clearTimeout(s),!r.ok){let o=await r.json().catch(()=>({detail:r.statusText}));throw new Error(o.detail||`HTTP ${r.status}`)}return r.status===204?null:await r.json()}catch(r){throw clearTimeout(s),r.name==="AbortError"?new Error("Request timeout"):r}}async _get(t,e=!1){if(e&&this._cache.has(t)){let a=this._cache.get(t);if(Date.now()-a.timestamp<this._cacheTimeout)return a.data}let i=await this._request(t);return e&&this._cache.set(t,{data:i,timestamp:Date.now()}),i}async _post(t,e){return this._request(t,{method:"POST",body:JSON.stringify(e)})}async _put(t,e){return this._request(t,{method:"PUT",body:JSON.stringify(e)})}async _delete(t){return this._request(t,{method:"DELETE"})}async getStatus(){return this._get("/api/status")}async healthCheck(){return this._get("/health")}async listProjects(t=null){let e=t?`?status=${t}`:"";return this._get(`/api/projects${e}`)}async getProject(t){return this._get(`/api/projects/${t}`)}async createProject(t){return this._post("/api/projects",t)}async updateProject(t,e){return this._put(`/api/projects/${t}`,e)}async deleteProject(t){return this._delete(`/api/projects/${t}`)}async listTasks(t={}){let e=new URLSearchParams;t.projectId&&e.append("project_id",t.projectId),t.status&&e.append("status",t.status),t.priority&&e.append("priority",t.priority);let i=e.toString()?`?${e}`:"";return this._get(`/api/tasks${i}`)}async getTask(t){return this._get(`/api/tasks/${t}`)}async createTask(t){return this._post("/api/tasks",t)}async updateTask(t,e){return this._put(`/api/tasks/${t}`,e)}async moveTask(t,e,i){return this._post(`/api/tasks/${t}/move`,{status:e,position:i})}async deleteTask(t){return this._delete(`/api/tasks/${t}`)}async getMemorySummary(){return this._get("/api/memory/summary",!0)}async getMemoryIndex(){return this._get("/api/memory/index",!0)}async getMemoryTimeline(){return this._get("/api/memory/timeline")}async listEpisodes(t={}){let e=new URLSearchParams(t).toString();return this._get(`/api/memory/episodes${e?"?"+e:""}`)}async getEpisode(t){return this._get(`/api/memory/episodes/${t}`)}async listPatterns(t={}){let e=new URLSearchParams(t).toString();return this._get(`/api/memory/patterns${e?"?"+e:""}`)}async getPattern(t){return this._get(`/api/memory/patterns/${t}`)}async listSkills(){return this._get("/api/memory/skills")}async getSkill(t){return this._get(`/api/memory/skills/${t}`)}async retrieveMemories(t,e=null,i=5){return this._post("/api/memory/retrieve",{query:t,taskType:e,topK:i})}async consolidateMemory(t=24){return this._post("/api/memory/consolidate",{sinceHours:t})}async getTokenEconomics(){return this._get("/api/memory/economics")}async searchMemory(t,e="all",i=20){let a=new URLSearchParams({q:t,collection:e,limit:String(i)});return this._get(`/api/memory/search?${a}`)}async getMemoryStats(){return this._get("/api/memory/stats",!0)}async listRegisteredProjects(t=!1){return this._get(`/api/registry/projects?include_inactive=${t}`)}async registerProject(t,e=null,i=null){return this._post("/api/registry/projects",{path:t,name:e,alias:i})}async discoverProjects(t=3){return this._get(`/api/registry/discover?max_depth=${t}`)}async syncRegistry(){return this._post("/api/registry/sync",{})}async getCrossProjectTasks(t=null){let e=t?`?project_ids=${t.join(",")}`:"";return this._get(`/api/registry/tasks${e}`)}async getLearningMetrics(t={}){let e=new URLSearchParams;t.timeRange&&e.append("timeRange",t.timeRange),t.signalType&&e.append("signalType",t.signalType),t.source&&e.append("source",t.source);let i=e.toString()?`?${e}`:"";return this._get(`/api/learning/metrics${i}`)}async getLearningTrends(t={}){let e=new URLSearchParams;t.timeRange&&e.append("timeRange",t.timeRange),t.signalType&&e.append("signalType",t.signalType),t.source&&e.append("source",t.source);let i=e.toString()?`?${e}`:"";return this._get(`/api/learning/trends${i}`)}async getLearningSignals(t={}){let e=new URLSearchParams;t.timeRange&&e.append("timeRange",t.timeRange),t.signalType&&e.append("signalType",t.signalType),t.source&&e.append("source",t.source),t.limit&&e.append("limit",String(t.limit)),t.offset&&e.append("offset",String(t.offset));let i=e.toString()?`?${e}`:"";return this._get(`/api/learning/signals${i}`)}async getLatestAggregation(){return this._get("/api/learning/aggregation")}async triggerAggregation(t={}){return this._post("/api/learning/aggregate",t)}async getAggregatedPreferences(t=20){return this._get(`/api/learning/preferences?limit=${t}`)}async getAggregatedErrors(t=20){return this._get(`/api/learning/errors?limit=${t}`)}async getAggregatedSuccessPatterns(t=20){return this._get(`/api/learning/success?limit=${t}`)}async getToolEfficiency(t=20){return this._get(`/api/learning/tools?limit=${t}`)}async getCost(){return this._get("/api/cost")}async getPricing(){return this._get("/api/pricing")}async getCouncilState(){return this._get("/api/council/state")}async getCouncilVerdicts(t=20){return this._get(`/api/council/verdicts?limit=${t}`)}async getCouncilConvergence(){return this._get("/api/council/convergence")}async getCouncilReport(){return this._get("/api/council/report")}async forceCouncilReview(){return this._post("/api/council/force-review",{})}async getContext(){return this._get("/api/context")}async getNotifications(t,e){let i=new URLSearchParams;t&&i.set("severity",t),e&&i.set("unread_only","true");let a=i.toString();return this._get("/api/notifications"+(a?"?"+a:""))}async getNotificationTriggers(){return this._get("/api/notifications/triggers")}async updateNotificationTriggers(t){return this._put("/api/notifications/triggers",{triggers:t})}async acknowledgeNotification(t){return this._post("/api/notifications/"+encodeURIComponent(t)+"/acknowledge",{})}async pauseSession(){return this._post("/api/control/pause",{})}async resumeSession(){return this._post("/api/control/resume",{})}async stopSession(){return this._post("/api/control/stop",{})}async getLogs(t=100){return this._get(`/api/logs?lines=${t}`)}async getChecklist(){return this._get("/api/checklist")}async getChecklistSummary(){return this._get("/api/checklist/summary")}async getPrdObservations(){let t=await fetch(`${this.baseUrl}/api/prd-observations`);if(!t.ok)throw new Error(`HTTP ${t.status}`);return t.text()}async getChecklistWaivers(){return this._get("/api/checklist/waivers")}async addChecklistWaiver(t,e,i="dashboard"){return this._post("/api/checklist/waivers",{item_id:t,reason:e,waived_by:i})}async removeChecklistWaiver(t){return this._delete(`/api/checklist/waivers/${encodeURIComponent(t)}`)}async getCouncilGate(){return this._get("/api/council/gate")}async getAppRunnerStatus(){return this._get("/api/app-runner/status")}async getAppRunnerLogs(t=100){return this._get(`/api/app-runner/logs?lines=${t}`)}async restartApp(){return this._post("/api/control/app-restart",{})}async stopApp(){return this._post("/api/control/app-stop",{})}async getPlaywrightResults(){return this._get("/api/playwright/results")}async getPlaywrightScreenshot(){return this._get("/api/playwright/screenshot")}startPolling(t,e=null){if(this._pollInterval)return;this._pollCallback=t;let i=async()=>{try{let s=await this.getStatus();this._connected=!0,this._pollCallback&&this._pollCallback(s),this._emit(u.STATUS_UPDATE,s),this._vscodeApi&&this.postToVSCode("pollSuccess",{timestamp:Date.now()})}catch(s){this._connected=!1,this._emit(u.ERROR,{error:s}),this._vscodeApi&&this.postToVSCode("pollError",{error:s.message})}};i();let a=e||this._currentPollInterval||this.config.pollInterval;this._pollInterval=setInterval(i,a)}stopPolling(){this._pollInterval&&(clearInterval(this._pollInterval),this._pollInterval=null)}};w(C,"_instances",new Map);var R=C;function wt(d={}){return new R(d)}function g(d={}){return R.getInstance(d)}var bt="loki-state-change",mt={ui:{theme:"light",sidebarCollapsed:!1,activeSection:"kanban",terminalAutoScroll:!0},session:{connected:!1,lastSync:null,mode:"offline",phase:null,iteration:null},localTasks:[],cache:{projects:[],tasks:[],agents:[],memory:null,lastFetch:null},preferences:{pollInterval:2e3,notifications:!0,soundEnabled:!1}},$=class $ extends EventTarget{static getInstance(){return $._instance||($._instance=new $),$._instance}constructor(){super(),this._state=this._loadState(),this._subscribers=new Map,this._batchUpdates=[],this._batchTimeout=null}_loadState(){try{let t=localStorage.getItem($.STORAGE_KEY);if(t){let e=JSON.parse(t);return this._mergeState(mt,e)}}catch(t){console.warn("Failed to load state from localStorage:",t)}return{...mt}}_mergeState(t,e){let i={...t};for(let a of Object.keys(e))a in t&&typeof t[a]=="object"&&!Array.isArray(t[a])?i[a]=this._mergeState(t[a],e[a]):i[a]=e[a];return i}_saveState(){try{let t={ui:this._state.ui,localTasks:this._state.localTasks,preferences:this._state.preferences};localStorage.setItem($.STORAGE_KEY,JSON.stringify(t))}catch(t){console.warn("Failed to save state to localStorage:",t)}}get(t=null){if(!t)return{...this._state};let e=t.split("."),i=this._state;for(let a of e){if(i==null)return;i=i[a]}return i}set(t,e,i=!0){let a=t.split("."),s=a.pop(),r=this._state;for(let n of a)n in r||(r[n]={}),r=r[n];let o=r[s];r[s]=e,i&&this._saveState(),this._notifyChange(t,e,o)}update(t,e=!0){let i=[];for(let[a,s]of Object.entries(t)){let r=this.get(a);this.set(a,s,!1),i.push({path:a,value:s,oldValue:r})}e&&this._saveState();for(let a of i)this._notifyChange(a.path,a.value,a.oldValue)}_notifyChange(t,e,i){this.dispatchEvent(new CustomEvent(bt,{detail:{path:t,value:e,oldValue:i}}));let a=this._subscribers.get(t)||[];for(let r of a)try{r(e,i,t)}catch(o){console.error("State subscriber error:",o)}let s=t.split(".");for(;s.length>1;){s.pop();let r=s.join("."),o=this._subscribers.get(r)||[];for(let n of o)try{n(this.get(r),null,r)}catch(l){console.error("State subscriber error:",l)}}}subscribe(t,e){return this._subscribers.has(t)||this._subscribers.set(t,[]),this._subscribers.get(t).push(e),()=>{let i=this._subscribers.get(t),a=i.indexOf(e);a>-1&&i.splice(a,1)}}reset(t=null){if(t){let e=t.split("."),i=mt;for(let a of e)i=i?.[a];this.set(t,i)}else this._state={...mt},this._saveState(),this.dispatchEvent(new CustomEvent(bt,{detail:{path:null,value:this._state,oldValue:null}}))}addLocalTask(t){let e=this.get("localTasks")||[],i={id:`local-${Date.now()}-${Math.random().toString(36).substr(2,9)}`,createdAt:new Date().toISOString(),status:"pending",...t};return this.set("localTasks",[...e,i]),i}updateLocalTask(t,e){let i=this.get("localTasks")||[],a=i.findIndex(r=>r.id===t);if(a===-1)return null;let s={...i[a],...e,updatedAt:new Date().toISOString()};return i[a]=s,this.set("localTasks",[...i]),s}deleteLocalTask(t){let e=this.get("localTasks")||[];this.set("localTasks",e.filter(i=>i.id!==t))}moveLocalTask(t,e,i=null){let s=(this.get("localTasks")||[]).find(r=>r.id===t);return s?this.updateLocalTask(t,{status:e,position:i??s.position}):null}updateSession(t){this.update(Object.fromEntries(Object.entries(t).map(([e,i])=>[`session.${e}`,i])),!1)}updateCache(t){this.update({"cache.projects":t.projects??this.get("cache.projects"),"cache.tasks":t.tasks??this.get("cache.tasks"),"cache.agents":t.agents??this.get("cache.agents"),"cache.memory":t.memory??this.get("cache.memory"),"cache.lastFetch":new Date().toISOString()},!1)}getMergedTasks(){let t=this.get("cache.tasks")||[],i=(this.get("localTasks")||[]).map(a=>({...a,isLocal:!0}));return[...t,...i]}getTasksByStatus(t){return this.getMergedTasks().filter(e=>e.status===t)}};w($,"STORAGE_KEY","loki-dashboard-state"),w($,"_instance",null);var F=$;function z(){return F.getInstance()}function $t(d){let t=z();return{get:()=>t.get(d),set:e=>t.set(d,e),subscribe:e=>t.subscribe(d,e)}}var j=class extends h{static get observedAttributes(){return["api-url","theme"]}constructor(){super(),this._data={status:"offline",phase:null,iteration:null,provider:null,running_agents:0,pending_tasks:null,uptime_seconds:0,complexity:null,connected:!1},this._api=null,this._pollInterval=null,this._statusUpdateHandler=null,this._connectedHandler=null,this._disconnectedHandler=null,this._checklistSummary=null,this._appRunnerStatus=null,this._playwrightResults=null,this._gateStatus=null}connectedCallback(){super.connectedCallback(),this._setupApi(),this._loadStatus(),this._startPolling()}disconnectedCallback(){super.disconnectedCallback(),this._stopPolling(),this._api&&(this._statusUpdateHandler&&this._api.removeEventListener(u.STATUS_UPDATE,this._statusUpdateHandler),this._connectedHandler&&this._api.removeEventListener(u.CONNECTED,this._connectedHandler),this._disconnectedHandler&&this._api.removeEventListener(u.DISCONNECTED,this._disconnectedHandler))}attributeChangedCallback(t,e,i){e!==i&&(t==="api-url"&&this._api&&(this._api.baseUrl=i,this._loadStatus()),t==="theme"&&this._applyTheme())}_setupApi(){let t=this.getAttribute("api-url")||window.location.origin;this._api=g({baseUrl:t}),this._statusUpdateHandler=e=>this._updateFromStatus(e.detail),this._connectedHandler=()=>{this._data.connected=!0,this.render()},this._disconnectedHandler=()=>{this._data.connected=!1,this._data.status="offline",this.render()},this._api.addEventListener(u.STATUS_UPDATE,this._statusUpdateHandler),this._api.addEventListener(u.CONNECTED,this._connectedHandler),this._api.addEventListener(u.DISCONNECTED,this._disconnectedHandler)}async _loadStatus(){try{let[t,e,i,a,s]=await Promise.allSettled([this._api.getStatus(),this._api.getChecklistSummary(),this._api.getAppRunnerStatus(),this._api.getPlaywrightResults(),this._api.getCouncilGate()]);t.status==="fulfilled"?this._updateFromStatus(t.value):(this._data.connected=!1,this._data.status="offline"),e.status==="fulfilled"&&(this._checklistSummary=e.value?.summary||null),i.status==="fulfilled"&&(this._appRunnerStatus=i.value),a.status==="fulfilled"&&(this._playwrightResults=a.value),s.status==="fulfilled"&&(this._gateStatus=s.value),this.render()}catch{this._data.connected=!1,this._data.status="offline",this.render()}}_updateFromStatus(t){t&&(this._data={...this._data,connected:!0,status:t.status||"offline",phase:t.phase||null,iteration:t.iteration!=null?t.iteration:null,provider:t.provider||null,running_agents:t.running_agents||0,pending_tasks:t.pending_tasks!=null?t.pending_tasks:null,uptime_seconds:t.uptime_seconds||0,complexity:t.complexity||null})}_startPolling(){this._pollInterval=setInterval(async()=>{try{await this._loadStatus()}catch{this._data.connected=!1,this._data.status="offline",this.render()}},5e3)}_stopPolling(){this._pollInterval&&(clearInterval(this._pollInterval),this._pollInterval=null)}_formatUptime(t){if(!t||t<0)return"--";let e=Math.floor(t/3600),i=Math.floor(t%3600/60),a=Math.floor(t%60);return e>0?`${e}h ${i}m`:i>0?`${i}m ${a}s`:`${a}s`}_getStatusDotClass(){switch(this._data.status){case"running":case"autonomous":return"active";case"paused":return"paused";case"stopped":return"stopped";case"error":return"error";default:return"offline"}}_escapeHtml(t){return t?String(t).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,"""):""}_renderAppRunnerCard(){let t=this._appRunnerStatus;if(!t||t.status==="not_initialized")return`
|
|
1240
|
+
`}getAriaPattern(t){return gt[t]||{}}applyAriaPattern(t,e){let i=this.getAriaPattern(e);for(let[a,s]of Object.entries(i))if(a==="role")t.setAttribute("role",s);else{let r=a.replace(/([A-Z])/g,"-$1").toLowerCase();t.setAttribute(r,s)}}render(){}};var L={realtime:1e3,normal:2e3,background:5e3,offline:1e4},_t={vscode:L.normal,browser:L.realtime,cli:L.background},yt={baseUrl:typeof window<"u"?window.location.origin:"http://localhost:57374",wsUrl:typeof window<"u"?`${window.location.protocol==="https:"?"wss:":"ws:"}//${window.location.host}/ws`:"ws://localhost:57374/ws",pollInterval:2e3,timeout:1e4,retryAttempts:3,retryDelay:1e3},u={CONNECTED:"api:connected",DISCONNECTED:"api:disconnected",ERROR:"api:error",STATUS_UPDATE:"api:status-update",TASK_CREATED:"api:task-created",TASK_UPDATED:"api:task-updated",TASK_DELETED:"api:task-deleted",PROJECT_CREATED:"api:project-created",PROJECT_UPDATED:"api:project-updated",AGENT_UPDATE:"api:agent-update",LOG_MESSAGE:"api:log-message",MEMORY_UPDATE:"api:memory-update",CHECKLIST_UPDATE:"api:checklist-update"},C=class C extends EventTarget{static getInstance(t={}){let e=t.baseUrl||yt.baseUrl;return C._instances.has(e)||C._instances.set(e,new C(t)),C._instances.get(e)}static clearInstances(){C._instances.forEach(t=>t.disconnect()),C._instances.clear()}constructor(t={}){super(),this.config={...yt,...t},this._ws=null,this._connected=!1,this._pollInterval=null,this._reconnectTimeout=null,this._reconnectAttempts=0,this._maxReconnectAttempts=20,this._cache=new Map,this._cacheTimeout=5e3,this._vscodeApi=null,this._context=this._detectContext(),this._currentPollInterval=_t[this._context]||L.normal,this._visibilityChangeHandler=null,this._messageHandler=null,this._setupAdaptivePolling(),this._setupVSCodeBridge()}_detectContext(){return typeof acquireVsCodeApi<"u"?"vscode":typeof window<"u"&&window.location?"browser":"cli"}get context(){return this._context}static get POLL_INTERVALS(){return L}_setupAdaptivePolling(){typeof document>"u"||(this._visibilityChangeHandler=()=>{document.hidden?this._setPollInterval(L.background):this._setPollInterval(_t[this._context]||L.normal)},document.addEventListener("visibilitychange",this._visibilityChangeHandler))}_setPollInterval(t){this._currentPollInterval=t,this._pollInterval&&(this.stopPolling(),this.startPolling(null,t))}setPollMode(t){let e=L[t];e&&this._setPollInterval(e)}_setupVSCodeBridge(){if(!(typeof acquireVsCodeApi>"u")){try{this._vscodeApi=acquireVsCodeApi()}catch{console.warn("VS Code API already acquired or unavailable");return}this._messageHandler=t=>{let e=t.data;if(!(!e||!e.type))switch(e.type){case"updateStatus":this._emit(u.STATUS_UPDATE,e.data);break;case"updateTasks":this._emit(u.TASK_UPDATED,e.data);break;case"taskCreated":this._emit(u.TASK_CREATED,e.data);break;case"taskDeleted":this._emit(u.TASK_DELETED,e.data);break;case"projectCreated":this._emit(u.PROJECT_CREATED,e.data);break;case"projectUpdated":this._emit(u.PROJECT_UPDATED,e.data);break;case"agentUpdate":this._emit(u.AGENT_UPDATE,e.data);break;case"logMessage":this._emit(u.LOG_MESSAGE,e.data);break;case"memoryUpdate":this._emit(u.MEMORY_UPDATE,e.data);break;case"connected":this._connected=!0,this._emit(u.CONNECTED,e.data);break;case"disconnected":this._connected=!1,this._emit(u.DISCONNECTED,e.data);break;case"error":this._emit(u.ERROR,e.data);break;case"setPollMode":this.setPollMode(e.data.mode);break;default:this._emit(`api:${e.type}`,e.data)}},window.addEventListener("message",this._messageHandler)}}get isVSCode(){return this._context==="vscode"}postToVSCode(t,e={}){this._vscodeApi&&this._vscodeApi.postMessage({type:t,data:e})}requestRefresh(){this.postToVSCode("requestRefresh")}notifyVSCode(t,e={}){this.postToVSCode("userAction",{action:t,...e})}get baseUrl(){return this.config.baseUrl}set baseUrl(t){this.config.baseUrl=t,this.config.wsUrl=t.replace(/^http/,"ws")+"/ws"}get isConnected(){return this._connected}async connect(){if(!(this._ws&&this._ws.readyState===WebSocket.OPEN))return new Promise((t,e)=>{try{this._ws=new WebSocket(this.config.wsUrl),this._ws.onopen=()=>{this._connected=!0,this._reconnectAttempts=0,this._emit(u.CONNECTED),t()},this._ws.onclose=()=>{this._connected=!1,this._emit(u.DISCONNECTED),this._scheduleReconnect()},this._ws.onerror=i=>{this._emit(u.ERROR,{error:i}),e(i)},this._ws.onmessage=i=>{try{let a=JSON.parse(i.data);this._handleMessage(a)}catch(a){console.error("Failed to parse WebSocket message:",a)}}}catch(i){e(i)}})}disconnect(){this._ws&&(this._ws.close(),this._ws=null),this._pollInterval&&(clearInterval(this._pollInterval),this._pollInterval=null),this._reconnectTimeout&&(clearTimeout(this._reconnectTimeout),this._reconnectTimeout=null),this._connected=!1,this._cleanupGlobalListeners()}_cleanupGlobalListeners(){this._visibilityChangeHandler&&typeof document<"u"&&(document.removeEventListener("visibilitychange",this._visibilityChangeHandler),this._visibilityChangeHandler=null),this._messageHandler&&typeof window<"u"&&(window.removeEventListener("message",this._messageHandler),this._messageHandler=null)}destroy(){this.disconnect()}_scheduleReconnect(){if(this._reconnectTimeout)return;if(this._reconnectAttempts>=this._maxReconnectAttempts){console.warn("WebSocket max reconnect attempts reached, giving up"),this._emit(u.ERROR,{error:"Max reconnect attempts reached"});return}let t=Math.min(this.config.retryDelay*Math.pow(2,this._reconnectAttempts),3e4);this._reconnectAttempts++,this._reconnectTimeout=setTimeout(()=>{this._reconnectTimeout=null,this.connect().catch(()=>{})},t)}_handleMessage(t){let i={connected:u.CONNECTED,status_update:u.STATUS_UPDATE,task_created:u.TASK_CREATED,task_updated:u.TASK_UPDATED,task_deleted:u.TASK_DELETED,task_moved:u.TASK_UPDATED,project_created:u.PROJECT_CREATED,project_updated:u.PROJECT_UPDATED,agent_update:u.AGENT_UPDATE,log:u.LOG_MESSAGE}[t.type]||`api:${t.type}`;this._emit(i,t.data)}_emit(t,e={}){this.dispatchEvent(new CustomEvent(t,{detail:e}))}async _request(t,e={}){let i=`${this.config.baseUrl}${t}`,a=new AbortController,s=setTimeout(()=>a.abort(),this.config.timeout);try{let r=await fetch(i,{...e,signal:a.signal,headers:{"Content-Type":"application/json",...e.headers}});if(clearTimeout(s),!r.ok){let o=await r.json().catch(()=>({detail:r.statusText}));throw new Error(o.detail||`HTTP ${r.status}`)}return r.status===204?null:await r.json()}catch(r){throw clearTimeout(s),r.name==="AbortError"?new Error("Request timeout"):r}}async _get(t,e=!1){if(e&&this._cache.has(t)){let a=this._cache.get(t);if(Date.now()-a.timestamp<this._cacheTimeout)return a.data}let i=await this._request(t);return e&&this._cache.set(t,{data:i,timestamp:Date.now()}),i}async _post(t,e){return this._request(t,{method:"POST",body:JSON.stringify(e)})}async _put(t,e){return this._request(t,{method:"PUT",body:JSON.stringify(e)})}async _delete(t){return this._request(t,{method:"DELETE"})}async getStatus(){return this._get("/api/status")}async healthCheck(){return this._get("/health")}async listProjects(t=null){let e=t?`?status=${t}`:"";return this._get(`/api/projects${e}`)}async getProject(t){return this._get(`/api/projects/${t}`)}async createProject(t){return this._post("/api/projects",t)}async updateProject(t,e){return this._put(`/api/projects/${t}`,e)}async deleteProject(t){return this._delete(`/api/projects/${t}`)}async listTasks(t={}){let e=new URLSearchParams;t.projectId&&e.append("project_id",t.projectId),t.status&&e.append("status",t.status),t.priority&&e.append("priority",t.priority);let i=e.toString()?`?${e}`:"";return this._get(`/api/tasks${i}`)}async getTask(t){return this._get(`/api/tasks/${t}`)}async createTask(t){return this._post("/api/tasks",t)}async updateTask(t,e){return this._put(`/api/tasks/${t}`,e)}async moveTask(t,e,i){return this._post(`/api/tasks/${t}/move`,{status:e,position:i})}async deleteTask(t){return this._delete(`/api/tasks/${t}`)}async getMemorySummary(){return this._get("/api/memory/summary",!0)}async getMemoryIndex(){return this._get("/api/memory/index",!0)}async getMemoryTimeline(){return this._get("/api/memory/timeline")}async listEpisodes(t={}){let e=new URLSearchParams(t).toString();return this._get(`/api/memory/episodes${e?"?"+e:""}`)}async getEpisode(t){return this._get(`/api/memory/episodes/${t}`)}async listPatterns(t={}){let e=new URLSearchParams(t).toString();return this._get(`/api/memory/patterns${e?"?"+e:""}`)}async getPattern(t){return this._get(`/api/memory/patterns/${t}`)}async listSkills(){return this._get("/api/memory/skills")}async getSkill(t){return this._get(`/api/memory/skills/${t}`)}async retrieveMemories(t,e=null,i=5){return this._post("/api/memory/retrieve",{query:t,taskType:e,topK:i})}async consolidateMemory(t=24){return this._post("/api/memory/consolidate",{sinceHours:t})}async getTokenEconomics(){return this._get("/api/memory/economics")}async searchMemory(t,e="all",i=20){let a=new URLSearchParams({q:t,collection:e,limit:String(i)});return this._get(`/api/memory/search?${a}`)}async getMemoryStats(){return this._get("/api/memory/stats",!0)}async listRegisteredProjects(t=!1){return this._get(`/api/registry/projects?include_inactive=${t}`)}async registerProject(t,e=null,i=null){return this._post("/api/registry/projects",{path:t,name:e,alias:i})}async discoverProjects(t=3){return this._get(`/api/registry/discover?max_depth=${t}`)}async syncRegistry(){return this._post("/api/registry/sync",{})}async getCrossProjectTasks(t=null){let e=t?`?project_ids=${t.join(",")}`:"";return this._get(`/api/registry/tasks${e}`)}async getLearningMetrics(t={}){let e=new URLSearchParams;t.timeRange&&e.append("timeRange",t.timeRange),t.signalType&&e.append("signalType",t.signalType),t.source&&e.append("source",t.source);let i=e.toString()?`?${e}`:"";return this._get(`/api/learning/metrics${i}`)}async getLearningTrends(t={}){let e=new URLSearchParams;t.timeRange&&e.append("timeRange",t.timeRange),t.signalType&&e.append("signalType",t.signalType),t.source&&e.append("source",t.source);let i=e.toString()?`?${e}`:"";return this._get(`/api/learning/trends${i}`)}async getLearningSignals(t={}){let e=new URLSearchParams;t.timeRange&&e.append("timeRange",t.timeRange),t.signalType&&e.append("signalType",t.signalType),t.source&&e.append("source",t.source),t.limit&&e.append("limit",String(t.limit)),t.offset&&e.append("offset",String(t.offset));let i=e.toString()?`?${e}`:"";return this._get(`/api/learning/signals${i}`)}async getLatestAggregation(){return this._get("/api/learning/aggregation")}async triggerAggregation(t={}){return this._post("/api/learning/aggregate",t)}async getAggregatedPreferences(t=20){return this._get(`/api/learning/preferences?limit=${t}`)}async getAggregatedErrors(t=20){return this._get(`/api/learning/errors?limit=${t}`)}async getAggregatedSuccessPatterns(t=20){return this._get(`/api/learning/success?limit=${t}`)}async getToolEfficiency(t=20){return this._get(`/api/learning/tools?limit=${t}`)}async getCost(){return this._get("/api/cost")}async getPricing(){return this._get("/api/pricing")}async getCouncilState(){return this._get("/api/council/state")}async getCouncilVerdicts(t=20){return this._get(`/api/council/verdicts?limit=${t}`)}async getCouncilConvergence(){return this._get("/api/council/convergence")}async getCouncilReport(){return this._get("/api/council/report")}async forceCouncilReview(){return this._post("/api/council/force-review",{})}async getContext(){return this._get("/api/context")}async getNotifications(t,e){let i=new URLSearchParams;t&&i.set("severity",t),e&&i.set("unread_only","true");let a=i.toString();return this._get("/api/notifications"+(a?"?"+a:""))}async getNotificationTriggers(){return this._get("/api/notifications/triggers")}async updateNotificationTriggers(t){return this._put("/api/notifications/triggers",{triggers:t})}async acknowledgeNotification(t){return this._post("/api/notifications/"+encodeURIComponent(t)+"/acknowledge",{})}async pauseSession(){return this._post("/api/control/pause",{})}async resumeSession(){return this._post("/api/control/resume",{})}async stopSession(){return this._post("/api/control/stop",{})}async getLogs(t=100){return this._get(`/api/logs?lines=${t}`)}async getChecklist(){return this._get("/api/checklist")}async getChecklistSummary(){return this._get("/api/checklist/summary")}async getPrdObservations(){let t=await fetch(`${this.baseUrl}/api/prd-observations`);if(!t.ok)throw new Error(`HTTP ${t.status}`);return t.text()}async getChecklistWaivers(){return this._get("/api/checklist/waivers")}async addChecklistWaiver(t,e,i="dashboard"){return this._post("/api/checklist/waivers",{item_id:t,reason:e,waived_by:i})}async removeChecklistWaiver(t){return this._delete(`/api/checklist/waivers/${encodeURIComponent(t)}`)}async getCouncilGate(){return this._get("/api/council/gate")}async getAppRunnerStatus(){return this._get("/api/app-runner/status")}async getAppRunnerLogs(t=100){return this._get(`/api/app-runner/logs?lines=${t}`)}async restartApp(){return this._post("/api/control/app-restart",{})}async stopApp(){return this._post("/api/control/app-stop",{})}async getPlaywrightResults(){return this._get("/api/playwright/results")}async getPlaywrightScreenshot(){return this._get("/api/playwright/screenshot")}startPolling(t,e=null){if(this._pollInterval)return;this._pollCallback=t;let i=async()=>{try{let s=await this.getStatus();this._connected=!0,this._pollCallback&&this._pollCallback(s),this._emit(u.STATUS_UPDATE,s),this._vscodeApi&&this.postToVSCode("pollSuccess",{timestamp:Date.now()})}catch(s){this._connected=!1,this._emit(u.ERROR,{error:s}),this._vscodeApi&&this.postToVSCode("pollError",{error:s.message})}};i();let a=e||this._currentPollInterval||this.config.pollInterval;this._pollInterval=setInterval(i,a)}stopPolling(){this._pollInterval&&(clearInterval(this._pollInterval),this._pollInterval=null)}};w(C,"_instances",new Map);var R=C;function wt(d={}){return new R(d)}function g(d={}){return R.getInstance(d)}var bt="loki-state-change",mt={ui:{theme:"light",sidebarCollapsed:!1,activeSection:"kanban",terminalAutoScroll:!0},session:{connected:!1,lastSync:null,mode:"offline",phase:null,iteration:null},localTasks:[],cache:{projects:[],tasks:[],agents:[],memory:null,lastFetch:null},preferences:{pollInterval:2e3,notifications:!0,soundEnabled:!1}},$=class $ extends EventTarget{static getInstance(){return $._instance||($._instance=new $),$._instance}constructor(){super(),this._state=this._loadState(),this._subscribers=new Map,this._batchUpdates=[],this._batchTimeout=null}_loadState(){try{let t=localStorage.getItem($.STORAGE_KEY);if(t){let e=JSON.parse(t);return this._mergeState(mt,e)}}catch(t){console.warn("Failed to load state from localStorage:",t)}return{...mt}}_mergeState(t,e){let i={...t};for(let a of Object.keys(e))a in t&&typeof t[a]=="object"&&!Array.isArray(t[a])?i[a]=this._mergeState(t[a],e[a]):i[a]=e[a];return i}_saveState(){try{let t={ui:this._state.ui,localTasks:this._state.localTasks,preferences:this._state.preferences};localStorage.setItem($.STORAGE_KEY,JSON.stringify(t))}catch(t){console.warn("Failed to save state to localStorage:",t)}}get(t=null){if(!t)return{...this._state};let e=t.split("."),i=this._state;for(let a of e){if(i==null)return;i=i[a]}return i}set(t,e,i=!0){let a=t.split("."),s=a.pop(),r=this._state;for(let n of a)n in r||(r[n]={}),r=r[n];let o=r[s];r[s]=e,i&&this._saveState(),this._notifyChange(t,e,o)}update(t,e=!0){let i=[];for(let[a,s]of Object.entries(t)){let r=this.get(a);this.set(a,s,!1),i.push({path:a,value:s,oldValue:r})}e&&this._saveState();for(let a of i)this._notifyChange(a.path,a.value,a.oldValue)}_notifyChange(t,e,i){this.dispatchEvent(new CustomEvent(bt,{detail:{path:t,value:e,oldValue:i}}));let a=this._subscribers.get(t)||[];for(let r of a)try{r(e,i,t)}catch(o){console.error("State subscriber error:",o)}let s=t.split(".");for(;s.length>1;){s.pop();let r=s.join("."),o=this._subscribers.get(r)||[];for(let n of o)try{n(this.get(r),null,r)}catch(l){console.error("State subscriber error:",l)}}}subscribe(t,e){return this._subscribers.has(t)||this._subscribers.set(t,[]),this._subscribers.get(t).push(e),()=>{let i=this._subscribers.get(t),a=i.indexOf(e);a>-1&&i.splice(a,1)}}reset(t=null){if(t){let e=t.split("."),i=mt;for(let a of e)i=i?.[a];this.set(t,i)}else this._state={...mt},this._saveState(),this.dispatchEvent(new CustomEvent(bt,{detail:{path:null,value:this._state,oldValue:null}}))}addLocalTask(t){let e=this.get("localTasks")||[],i={id:`local-${Date.now()}-${Math.random().toString(36).substr(2,9)}`,createdAt:new Date().toISOString(),status:"pending",...t};return this.set("localTasks",[...e,i]),i}updateLocalTask(t,e){let i=this.get("localTasks")||[],a=i.findIndex(r=>r.id===t);if(a===-1)return null;let s={...i[a],...e,updatedAt:new Date().toISOString()};return i[a]=s,this.set("localTasks",[...i]),s}deleteLocalTask(t){let e=this.get("localTasks")||[];this.set("localTasks",e.filter(i=>i.id!==t))}moveLocalTask(t,e,i=null){let s=(this.get("localTasks")||[]).find(r=>r.id===t);return s?this.updateLocalTask(t,{status:e,position:i??s.position}):null}updateSession(t){this.update(Object.fromEntries(Object.entries(t).map(([e,i])=>[`session.${e}`,i])),!1)}updateCache(t){this.update({"cache.projects":t.projects??this.get("cache.projects"),"cache.tasks":t.tasks??this.get("cache.tasks"),"cache.agents":t.agents??this.get("cache.agents"),"cache.memory":t.memory??this.get("cache.memory"),"cache.lastFetch":new Date().toISOString()},!1)}getMergedTasks(){let t=this.get("cache.tasks")||[],i=(this.get("localTasks")||[]).map(a=>({...a,isLocal:!0}));return[...t,...i]}getTasksByStatus(t){return this.getMergedTasks().filter(e=>e.status===t)}};w($,"STORAGE_KEY","loki-dashboard-state"),w($,"_instance",null);var F=$;function z(){return F.getInstance()}function $t(d){let t=z();return{get:()=>t.get(d),set:e=>t.set(d,e),subscribe:e=>t.subscribe(d,e)}}var j=class extends h{static get observedAttributes(){return["api-url","theme"]}constructor(){super(),this._data={status:"offline",phase:null,iteration:null,provider:null,running_agents:0,pending_tasks:null,uptime_seconds:0,complexity:null,connected:!1},this._api=null,this._pollInterval=null,this._statusUpdateHandler=null,this._connectedHandler=null,this._disconnectedHandler=null,this._checklistSummary=null,this._appRunnerStatus=null,this._playwrightResults=null,this._gateStatus=null}connectedCallback(){super.connectedCallback(),this._setupApi(),this._loadStatus(),this._startPolling(),this._api.connect().catch(()=>{})}disconnectedCallback(){super.disconnectedCallback(),this._stopPolling(),this._api&&(this._statusUpdateHandler&&this._api.removeEventListener(u.STATUS_UPDATE,this._statusUpdateHandler),this._connectedHandler&&this._api.removeEventListener(u.CONNECTED,this._connectedHandler),this._disconnectedHandler&&this._api.removeEventListener(u.DISCONNECTED,this._disconnectedHandler))}attributeChangedCallback(t,e,i){e!==i&&(t==="api-url"&&this._api&&(this._api.baseUrl=i,this._loadStatus()),t==="theme"&&this._applyTheme())}_setupApi(){let t=this.getAttribute("api-url")||window.location.origin;this._api=g({baseUrl:t}),this._statusUpdateHandler=e=>this._updateFromStatus(e.detail),this._connectedHandler=()=>{this._data.connected=!0,this.render()},this._disconnectedHandler=()=>{this._data.connected=!1,this._data.status="offline",this.render()},this._api.addEventListener(u.STATUS_UPDATE,this._statusUpdateHandler),this._api.addEventListener(u.CONNECTED,this._connectedHandler),this._api.addEventListener(u.DISCONNECTED,this._disconnectedHandler)}async _loadStatus(){try{let[t,e,i,a,s]=await Promise.allSettled([this._api.getStatus(),this._api.getChecklistSummary(),this._api.getAppRunnerStatus(),this._api.getPlaywrightResults(),this._api.getCouncilGate()]);t.status==="fulfilled"?this._updateFromStatus(t.value):(this._data.connected=!1,this._data.status="offline"),e.status==="fulfilled"&&(this._checklistSummary=e.value?.summary||null),i.status==="fulfilled"&&(this._appRunnerStatus=i.value),a.status==="fulfilled"&&(this._playwrightResults=a.value),s.status==="fulfilled"&&(this._gateStatus=s.value),this.render()}catch{this._data.connected=!1,this._data.status="offline",this.render()}}_updateFromStatus(t){t&&(this._data={...this._data,connected:!0,status:t.status||"offline",phase:t.phase||null,iteration:t.iteration!=null?t.iteration:null,provider:t.provider||null,running_agents:t.running_agents||0,pending_tasks:t.pending_tasks!=null?t.pending_tasks:null,uptime_seconds:t.uptime_seconds||0,complexity:t.complexity||null})}_startPolling(){this._pollInterval=setInterval(async()=>{try{await this._loadStatus()}catch{this._data.connected=!1,this._data.status="offline",this.render()}},5e3)}_stopPolling(){this._pollInterval&&(clearInterval(this._pollInterval),this._pollInterval=null)}_formatUptime(t){if(!t||t<0)return"--";let e=Math.floor(t/3600),i=Math.floor(t%3600/60),a=Math.floor(t%60);return e>0?`${e}h ${i}m`:i>0?`${i}m ${a}s`:`${a}s`}_getStatusDotClass(){switch(this._data.status){case"running":case"autonomous":return"active";case"paused":return"paused";case"stopped":return"stopped";case"error":return"error";default:return"offline"}}_escapeHtml(t){return t?String(t).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,"""):""}_renderAppRunnerCard(){let t=this._appRunnerStatus;if(!t||t.status==="not_initialized")return`
|
|
1241
1241
|
<div class="overview-card">
|
|
1242
1242
|
<div class="card-label">App Runner</div>
|
|
1243
1243
|
<div class="card-value small-text">${this._data.status==="running"||this._data.status==="autonomous"?"Waiting...":"Not started"}</div>
|
package/docs/INSTALLATION.md
CHANGED
package/mcp/__init__.py
CHANGED