memstack-skill-loader 4.0.1__tar.gz → 4.0.3__tar.gz
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.
- {memstack_skill_loader-4.0.1/src/memstack_skill_loader.egg-info → memstack_skill_loader-4.0.3}/PKG-INFO +1 -1
- {memstack_skill_loader-4.0.1 → memstack_skill_loader-4.0.3}/pyproject.toml +1 -1
- {memstack_skill_loader-4.0.1 → memstack_skill_loader-4.0.3}/src/memstack_skill_loader/__init__.py +1 -1
- {memstack_skill_loader-4.0.1 → memstack_skill_loader-4.0.3}/src/memstack_skill_loader/agent_runner.py +104 -29
- {memstack_skill_loader-4.0.1 → memstack_skill_loader-4.0.3}/src/memstack_skill_loader/dashboard.html +53 -9
- {memstack_skill_loader-4.0.1 → memstack_skill_loader-4.0.3/src/memstack_skill_loader.egg-info}/PKG-INFO +1 -1
- {memstack_skill_loader-4.0.1 → memstack_skill_loader-4.0.3}/MANIFEST.in +0 -0
- {memstack_skill_loader-4.0.1 → memstack_skill_loader-4.0.3}/README.md +0 -0
- {memstack_skill_loader-4.0.1 → memstack_skill_loader-4.0.3}/setup.cfg +0 -0
- {memstack_skill_loader-4.0.1 → memstack_skill_loader-4.0.3}/src/memstack_skill_loader/__main__.py +0 -0
- {memstack_skill_loader-4.0.1 → memstack_skill_loader-4.0.3}/src/memstack_skill_loader/categories.py +0 -0
- {memstack_skill_loader-4.0.1 → memstack_skill_loader-4.0.3}/src/memstack_skill_loader/compression.py +0 -0
- {memstack_skill_loader-4.0.1 → memstack_skill_loader-4.0.3}/src/memstack_skill_loader/config.py +0 -0
- {memstack_skill_loader-4.0.1 → memstack_skill_loader-4.0.3}/src/memstack_skill_loader/dashboard.py +0 -0
- {memstack_skill_loader-4.0.1 → memstack_skill_loader-4.0.3}/src/memstack_skill_loader/indexer.py +0 -0
- {memstack_skill_loader-4.0.1 → memstack_skill_loader-4.0.3}/src/memstack_skill_loader/license.py +0 -0
- {memstack_skill_loader-4.0.1 → memstack_skill_loader-4.0.3}/src/memstack_skill_loader/memory_db.py +0 -0
- {memstack_skill_loader-4.0.1 → memstack_skill_loader-4.0.3}/src/memstack_skill_loader/search.py +0 -0
- {memstack_skill_loader-4.0.1 → memstack_skill_loader-4.0.3}/src/memstack_skill_loader/server.py +0 -0
- {memstack_skill_loader-4.0.1 → memstack_skill_loader-4.0.3}/src/memstack_skill_loader/skill_config.py +0 -0
- {memstack_skill_loader-4.0.1 → memstack_skill_loader-4.0.3}/src/memstack_skill_loader/stats.py +0 -0
- {memstack_skill_loader-4.0.1 → memstack_skill_loader-4.0.3}/src/memstack_skill_loader/tfidf_search.py +0 -0
- {memstack_skill_loader-4.0.1 → memstack_skill_loader-4.0.3}/src/memstack_skill_loader/version_check.py +0 -0
- {memstack_skill_loader-4.0.1 → memstack_skill_loader-4.0.3}/src/memstack_skill_loader.egg-info/SOURCES.txt +0 -0
- {memstack_skill_loader-4.0.1 → memstack_skill_loader-4.0.3}/src/memstack_skill_loader.egg-info/dependency_links.txt +0 -0
- {memstack_skill_loader-4.0.1 → memstack_skill_loader-4.0.3}/src/memstack_skill_loader.egg-info/entry_points.txt +0 -0
- {memstack_skill_loader-4.0.1 → memstack_skill_loader-4.0.3}/src/memstack_skill_loader.egg-info/requires.txt +0 -0
- {memstack_skill_loader-4.0.1 → memstack_skill_loader-4.0.3}/src/memstack_skill_loader.egg-info/top_level.txt +0 -0
|
@@ -295,12 +295,45 @@ def _invoke_agent(name: str, prompt: str, working_dir: str, log_path: Optional[P
|
|
|
295
295
|
)
|
|
296
296
|
with _lock:
|
|
297
297
|
_current_process = proc
|
|
298
|
+
|
|
299
|
+
stdout_chunks: list[str] = []
|
|
300
|
+
stderr_chunks: list[str] = []
|
|
301
|
+
|
|
302
|
+
def _reader(stream: object, dest: list[str]) -> None:
|
|
303
|
+
try:
|
|
304
|
+
for line in stream: # type: ignore[union-attr]
|
|
305
|
+
dest.append(line)
|
|
306
|
+
except Exception:
|
|
307
|
+
pass
|
|
308
|
+
|
|
309
|
+
t_out = threading.Thread(target=_reader, args=(proc.stdout, stdout_chunks), daemon=True)
|
|
310
|
+
t_err = threading.Thread(target=_reader, args=(proc.stderr, stderr_chunks), daemon=True)
|
|
311
|
+
|
|
298
312
|
try:
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
313
|
+
if proc.stdin:
|
|
314
|
+
try:
|
|
315
|
+
proc.stdin.write(prompt)
|
|
316
|
+
proc.stdin.close()
|
|
317
|
+
except OSError:
|
|
318
|
+
pass
|
|
319
|
+
t_out.start()
|
|
320
|
+
t_err.start()
|
|
321
|
+
|
|
322
|
+
deadline = time.monotonic() + timeout
|
|
323
|
+
while True:
|
|
324
|
+
t_out.join(timeout=30)
|
|
325
|
+
rc = proc.poll()
|
|
326
|
+
if rc is not None:
|
|
327
|
+
break
|
|
328
|
+
if time.monotonic() > deadline:
|
|
329
|
+
proc.kill()
|
|
330
|
+
t_out.join(5)
|
|
331
|
+
t_err.join(5)
|
|
332
|
+
raise subprocess.TimeoutExpired(cmd, timeout)
|
|
333
|
+
t_out.join(5)
|
|
334
|
+
t_err.join(5)
|
|
335
|
+
stdout_data = "".join(stdout_chunks)
|
|
336
|
+
stderr_data = "".join(stderr_chunks)
|
|
304
337
|
finally:
|
|
305
338
|
with _lock:
|
|
306
339
|
if _current_process is proc:
|
|
@@ -367,9 +400,9 @@ class Session:
|
|
|
367
400
|
self.started_at = time.strftime("%Y-%m-%d %H:%M:%S")
|
|
368
401
|
self.messages: list[dict] = []
|
|
369
402
|
self.agents: dict[str, dict] = {
|
|
370
|
-
"manager": {"status": "idle", "input_tokens": 0, "output_tokens": 0, "context_tokens": 0, "last_output": ""},
|
|
371
|
-
"builder": {"status": "idle", "input_tokens": 0, "output_tokens": 0, "context_tokens": 0, "last_output": ""},
|
|
372
|
-
"reviewer": {"status": "idle", "input_tokens": 0, "output_tokens": 0, "context_tokens": 0, "last_output": ""},
|
|
403
|
+
"manager": {"status": "idle", "input_tokens": 0, "output_tokens": 0, "context_tokens": 0, "last_output": "", "started_at": None},
|
|
404
|
+
"builder": {"status": "idle", "input_tokens": 0, "output_tokens": 0, "context_tokens": 0, "last_output": "", "started_at": None},
|
|
405
|
+
"reviewer": {"status": "idle", "input_tokens": 0, "output_tokens": 0, "context_tokens": 0, "last_output": "", "started_at": None},
|
|
373
406
|
}
|
|
374
407
|
|
|
375
408
|
def add_message(self, from_agent: str, to_agent: str, content: str) -> None:
|
|
@@ -471,6 +504,7 @@ def _orchestrate(session: Session) -> None:
|
|
|
471
504
|
try:
|
|
472
505
|
# Step 1: Manager analyzes the task
|
|
473
506
|
session.agents["manager"]["status"] = "busy"
|
|
507
|
+
session.agents["manager"]["started_at"] = time.strftime("%Y-%m-%dT%H:%M:%S")
|
|
474
508
|
session._save_state()
|
|
475
509
|
|
|
476
510
|
context = _gather_project_context(session.working_dir)
|
|
@@ -480,13 +514,26 @@ def _orchestrate(session: Session) -> None:
|
|
|
480
514
|
manager_prompt += "\n\nAdditional context from user:\n" + session.context
|
|
481
515
|
if session.user_name:
|
|
482
516
|
manager_prompt = f"The user's name is {session.user_name}.\n\n" + manager_prompt
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
517
|
+
try:
|
|
518
|
+
manager_output, m_in, m_out, m_ctx = _invoke_agent(
|
|
519
|
+
"manager", manager_prompt, session.working_dir,
|
|
520
|
+
log_path=session_log_dir / "manager.log",
|
|
521
|
+
skip_permissions=True, session_id=session.session_id,
|
|
522
|
+
timeout=min(180, session.timeout),
|
|
523
|
+
model=session.models.get("manager", ""),
|
|
524
|
+
)
|
|
525
|
+
except subprocess.TimeoutExpired:
|
|
526
|
+
session.agents["manager"]["status"] = "timeout"
|
|
527
|
+
session.status = "error"
|
|
528
|
+
session.result = "Manager timed out after 3 minutes. Try a simpler task description or break the task into smaller pieces."
|
|
529
|
+
session._save_state()
|
|
530
|
+
return
|
|
531
|
+
except RuntimeError:
|
|
532
|
+
session.agents["manager"]["status"] = "crashed"
|
|
533
|
+
session.status = "error"
|
|
534
|
+
session.result = "Manager agent stopped unexpectedly."
|
|
535
|
+
session._save_state()
|
|
536
|
+
return
|
|
490
537
|
session.agents["manager"]["input_tokens"] += m_in
|
|
491
538
|
session.agents["manager"]["output_tokens"] += m_out
|
|
492
539
|
session.agents["manager"]["context_tokens"] = m_ctx
|
|
@@ -515,6 +562,7 @@ def _orchestrate(session: Session) -> None:
|
|
|
515
562
|
|
|
516
563
|
# Builder
|
|
517
564
|
session.agents["builder"]["status"] = "busy"
|
|
565
|
+
session.agents["builder"]["started_at"] = time.strftime("%Y-%m-%dT%H:%M:%S")
|
|
518
566
|
session._save_state()
|
|
519
567
|
|
|
520
568
|
builder_prompt = (
|
|
@@ -526,13 +574,26 @@ def _orchestrate(session: Session) -> None:
|
|
|
526
574
|
)
|
|
527
575
|
if session.user_name:
|
|
528
576
|
builder_prompt = f"The user's name is {session.user_name}.\n\n" + builder_prompt
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
577
|
+
try:
|
|
578
|
+
builder_output, b_in, b_out, b_ctx = _invoke_agent(
|
|
579
|
+
"builder", builder_prompt, session.working_dir,
|
|
580
|
+
log_path=session_log_dir / "builder.log",
|
|
581
|
+
skip_permissions=True, bare=True, session_id=session.session_id,
|
|
582
|
+
timeout=session.timeout,
|
|
583
|
+
model=session.models.get("builder", ""),
|
|
584
|
+
)
|
|
585
|
+
except subprocess.TimeoutExpired:
|
|
586
|
+
session.agents["builder"]["status"] = "timeout"
|
|
587
|
+
session.status = "error"
|
|
588
|
+
session.result = "Builder timed out. The task may be too large for a single agent pass."
|
|
589
|
+
session._save_state()
|
|
590
|
+
return
|
|
591
|
+
except RuntimeError:
|
|
592
|
+
session.agents["builder"]["status"] = "crashed"
|
|
593
|
+
session.status = "error"
|
|
594
|
+
session.result = "Builder agent stopped unexpectedly."
|
|
595
|
+
session._save_state()
|
|
596
|
+
return
|
|
536
597
|
session.agents["builder"]["input_tokens"] += b_in
|
|
537
598
|
session.agents["builder"]["output_tokens"] += b_out
|
|
538
599
|
session.agents["builder"]["context_tokens"] = b_ctx
|
|
@@ -568,6 +629,7 @@ def _orchestrate(session: Session) -> None:
|
|
|
568
629
|
|
|
569
630
|
# Reviewer
|
|
570
631
|
session.agents["reviewer"]["status"] = "busy"
|
|
632
|
+
session.agents["reviewer"]["started_at"] = time.strftime("%Y-%m-%dT%H:%M:%S")
|
|
571
633
|
session._save_state()
|
|
572
634
|
|
|
573
635
|
reviewer_prompt = (
|
|
@@ -575,13 +637,26 @@ def _orchestrate(session: Session) -> None:
|
|
|
575
637
|
+ f"\n\nOriginal task from user:\n{session.task}"
|
|
576
638
|
+ f"\n\nBuilder output (iteration {iteration}):\n{builder_output}"
|
|
577
639
|
)
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
640
|
+
try:
|
|
641
|
+
reviewer_output, r_in, r_out, r_ctx = _invoke_agent(
|
|
642
|
+
"reviewer", reviewer_prompt, session.working_dir,
|
|
643
|
+
log_path=session_log_dir / "reviewer.log",
|
|
644
|
+
bare=True, session_id=session.session_id,
|
|
645
|
+
timeout=session.timeout,
|
|
646
|
+
model=session.models.get("reviewer", ""),
|
|
647
|
+
)
|
|
648
|
+
except subprocess.TimeoutExpired:
|
|
649
|
+
session.agents["reviewer"]["status"] = "timeout"
|
|
650
|
+
session.status = "error"
|
|
651
|
+
session.result = "Reviewer timed out."
|
|
652
|
+
session._save_state()
|
|
653
|
+
return
|
|
654
|
+
except RuntimeError:
|
|
655
|
+
session.agents["reviewer"]["status"] = "crashed"
|
|
656
|
+
session.status = "error"
|
|
657
|
+
session.result = "Reviewer agent stopped unexpectedly."
|
|
658
|
+
session._save_state()
|
|
659
|
+
return
|
|
585
660
|
session.agents["reviewer"]["input_tokens"] += r_in
|
|
586
661
|
session.agents["reviewer"]["output_tokens"] += r_out
|
|
587
662
|
session.agents["reviewer"]["context_tokens"] = r_ctx
|
{memstack_skill_loader-4.0.1 → memstack_skill_loader-4.0.3}/src/memstack_skill_loader/dashboard.html
RENAMED
|
@@ -1449,7 +1449,7 @@
|
|
|
1449
1449
|
<summary class="model-selection-summary">Model Selection</summary>
|
|
1450
1450
|
<div class="model-selection-grid">
|
|
1451
1451
|
<div class="model-selection-col">
|
|
1452
|
-
<label class="model-selection-label">Manager</label>
|
|
1452
|
+
<label id="agent-model-label-manager" class="model-selection-label">Manager</label>
|
|
1453
1453
|
<select id="agent-model-manager" class="model-selection-select">
|
|
1454
1454
|
<option value="claude-opus-4-6">claude-opus-4-6</option>
|
|
1455
1455
|
<option value="claude-sonnet-4-6">claude-sonnet-4-6</option>
|
|
@@ -1457,7 +1457,7 @@
|
|
|
1457
1457
|
</select>
|
|
1458
1458
|
</div>
|
|
1459
1459
|
<div class="model-selection-col">
|
|
1460
|
-
<label class="model-selection-label">Builder</label>
|
|
1460
|
+
<label id="agent-model-label-builder" class="model-selection-label">Builder</label>
|
|
1461
1461
|
<select id="agent-model-builder" class="model-selection-select">
|
|
1462
1462
|
<option value="claude-opus-4-6">claude-opus-4-6</option>
|
|
1463
1463
|
<option value="claude-sonnet-4-6">claude-sonnet-4-6</option>
|
|
@@ -1465,7 +1465,7 @@
|
|
|
1465
1465
|
</select>
|
|
1466
1466
|
</div>
|
|
1467
1467
|
<div class="model-selection-col">
|
|
1468
|
-
<label class="model-selection-label">Reviewer</label>
|
|
1468
|
+
<label id="agent-model-label-reviewer" class="model-selection-label">Reviewer</label>
|
|
1469
1469
|
<select id="agent-model-reviewer" class="model-selection-select">
|
|
1470
1470
|
<option value="claude-opus-4-6">claude-opus-4-6</option>
|
|
1471
1471
|
<option value="claude-sonnet-4-6">claude-sonnet-4-6</option>
|
|
@@ -2730,7 +2730,8 @@ document.addEventListener('visibilitychange', () => {
|
|
|
2730
2730
|
const STATUS_COLORS = {
|
|
2731
2731
|
idle: '#8b949e', busy: '#58a6ff', done: '#3fb950',
|
|
2732
2732
|
running: '#3fb950', stopped: '#6e7681', error: '#f85149',
|
|
2733
|
-
starting: '#d2a8ff', completed: '#3fb950'
|
|
2733
|
+
starting: '#d2a8ff', completed: '#3fb950',
|
|
2734
|
+
timeout: '#f0883e', crashed: '#f85149'
|
|
2734
2735
|
};
|
|
2735
2736
|
|
|
2736
2737
|
const ROLE_NAME_COLORS = {manager: '#d2a8ff', builder: '#58a6ff', reviewer: '#7ee787'};
|
|
@@ -2742,6 +2743,7 @@ const BUSY_PHRASES = {
|
|
|
2742
2743
|
};
|
|
2743
2744
|
let busyPhraseIntervals = {};
|
|
2744
2745
|
let busyPhraseIndices = {manager: 0, builder: 0, reviewer: 0};
|
|
2746
|
+
let elapsedIntervals = {};
|
|
2745
2747
|
|
|
2746
2748
|
const MODEL_DEFAULTS = {manager: 'claude-opus-4-6', builder: 'claude-sonnet-4-6', reviewer: 'claude-sonnet-4-6'};
|
|
2747
2749
|
|
|
@@ -2778,6 +2780,8 @@ async function resetAgentUI() {
|
|
|
2778
2780
|
lastGitSuccess = true;
|
|
2779
2781
|
const dsCard = document.getElementById('diary-summary-card');
|
|
2780
2782
|
if (dsCard) { dsCard.style.display = 'none'; dsCard.innerHTML = ''; }
|
|
2783
|
+
document.getElementById('agent-workdir-input').value = '';
|
|
2784
|
+
loadLastWorkdir();
|
|
2781
2785
|
}
|
|
2782
2786
|
|
|
2783
2787
|
async function startAgentTask() {
|
|
@@ -2833,6 +2837,12 @@ async function loadAgentMonitor() {
|
|
|
2833
2837
|
if (name) opt.textContent = name;
|
|
2834
2838
|
}
|
|
2835
2839
|
}
|
|
2840
|
+
for (const role of ['manager', 'builder', 'reviewer']) {
|
|
2841
|
+
const lbl = document.getElementById('agent-model-label-' + role);
|
|
2842
|
+
if (lbl && userProfile.agent_names && userProfile.agent_names[role]) {
|
|
2843
|
+
lbl.textContent = userProfile.agent_names[role];
|
|
2844
|
+
}
|
|
2845
|
+
}
|
|
2836
2846
|
fetchAgentStatus();
|
|
2837
2847
|
loadRecentProjects();
|
|
2838
2848
|
loadLastWorkdir();
|
|
@@ -3111,10 +3121,17 @@ function renderAgentUI(data) {
|
|
|
3111
3121
|
launcher.style.display = 'none';
|
|
3112
3122
|
active.style.display = 'none';
|
|
3113
3123
|
completed.style.display = '';
|
|
3114
|
-
const
|
|
3124
|
+
const hasTimeout = Object.values(agents).some(ag => ag.status === 'timeout');
|
|
3125
|
+
const hasCrash = Object.values(agents).some(ag => ag.status === 'crashed');
|
|
3126
|
+
let icon, title;
|
|
3127
|
+
if (status === 'completed') { icon = '✅'; title = 'Task Completed'; }
|
|
3128
|
+
else if (status === 'stopped') { icon = '⏹'; title = 'Task Stopped'; }
|
|
3129
|
+
else if (hasTimeout) { icon = '⏳'; title = 'Task Timed Out'; }
|
|
3130
|
+
else if (hasCrash) { icon = '⚠️'; title = 'Task Failed'; }
|
|
3131
|
+
else { icon = '⚠️'; title = 'Task Failed'; }
|
|
3115
3132
|
document.querySelector('#agent-completed div div:first-child').innerHTML = icon;
|
|
3116
3133
|
const h2 = document.querySelector('#agent-completed h2');
|
|
3117
|
-
if (h2) h2.textContent =
|
|
3134
|
+
if (h2) h2.textContent = title;
|
|
3118
3135
|
document.getElementById('agent-result-summary').textContent = status === 'stopped' ? (data.result || 'Task stopped by user') : (data.result || `Session ${status}`);
|
|
3119
3136
|
const COMPACTION_LIMIT = 200000;
|
|
3120
3137
|
const COMPACTION_WARN = 155000;
|
|
@@ -3218,8 +3235,12 @@ function renderAgentUI(data) {
|
|
|
3218
3235
|
const phrase = BUSY_PHRASES[role][busyPhraseIndices[role] % BUSY_PHRASES[role].length];
|
|
3219
3236
|
const customName = (userProfile.agent_names && userProfile.agent_names[role]) || role.charAt(0).toUpperCase() + role.slice(1);
|
|
3220
3237
|
statusDisplay = `<span class="agent-status-phrase" id="agent-phrase-${role}"><span class="agent-name-label" style="color:${ROLE_NAME_COLORS[role] || '#e6edf3'}">${escapeHtml(customName)}:</span> ${escapeHtml(phrase)}</span>`;
|
|
3221
|
-
} else if (
|
|
3222
|
-
|
|
3238
|
+
} else if (a.status === 'timeout') {
|
|
3239
|
+
statusDisplay = 'Timed out';
|
|
3240
|
+
} else if (a.status === 'crashed') {
|
|
3241
|
+
statusDisplay = 'Crashed';
|
|
3242
|
+
} else if ((a.status === 'done' || a.status === 'completed') && (a.started_at || data.started_at)) {
|
|
3243
|
+
const startMs = new Date(a.started_at || data.started_at).getTime();
|
|
3223
3244
|
const elapsed = Math.max(0, Math.round((Date.now() - startMs) / 1000));
|
|
3224
3245
|
const mins = Math.floor(elapsed / 60);
|
|
3225
3246
|
const secs = elapsed % 60;
|
|
@@ -3227,6 +3248,15 @@ function renderAgentUI(data) {
|
|
|
3227
3248
|
} else {
|
|
3228
3249
|
statusDisplay = a.status;
|
|
3229
3250
|
}
|
|
3251
|
+
let elapsedHtml = '';
|
|
3252
|
+
if (a.status === 'busy' && a.started_at) {
|
|
3253
|
+
const secs = Math.max(0, Math.floor((Date.now() - new Date(a.started_at).getTime()) / 1000));
|
|
3254
|
+
elapsedHtml = `<div class="agent-elapsed" id="agent-elapsed-${role}" style="font-size:0.7rem;color:#8b949e;margin-top:0.3rem;">Running: ${Math.floor(secs/60)}m ${secs%60}s</div>`;
|
|
3255
|
+
} else if ((a.status === 'done' || a.status === 'completed' || a.status === 'timeout' || a.status === 'crashed') && a.started_at) {
|
|
3256
|
+
const secs = Math.max(0, Math.floor((Date.now() - new Date(a.started_at).getTime()) / 1000));
|
|
3257
|
+
const label = a.status === 'timeout' ? 'Timed out after' : a.status === 'crashed' ? 'Crashed after' : 'Completed in';
|
|
3258
|
+
elapsedHtml = `<div style="font-size:0.7rem;color:#8b949e;margin-top:0.3rem;">${label} ${Math.floor(secs/60)}m ${secs%60}s</div>`;
|
|
3259
|
+
}
|
|
3230
3260
|
const COMPACTION_LIMIT = 200000;
|
|
3231
3261
|
const tokens = (a.context_tokens || 0);
|
|
3232
3262
|
const pct = tokens > 0 ? Math.min(100, Math.round(tokens / COMPACTION_LIMIT * 100)) : -1;
|
|
@@ -3245,7 +3275,8 @@ function renderAgentUI(data) {
|
|
|
3245
3275
|
showToast(role + ' context at ' + pct + '% — diary auto-saved', 'warning', 5000);
|
|
3246
3276
|
}
|
|
3247
3277
|
}
|
|
3248
|
-
|
|
3278
|
+
const errorBorder = (a.status === 'error' || a.status === 'timeout' || a.status === 'crashed') ? ' agent-card-error-border' : '';
|
|
3279
|
+
return `<div class="agent-card${errorBorder}" style="${pulseStyle}">
|
|
3249
3280
|
<span class="agent-tooltip">${escapeHtml(ROLE_DESCRIPTIONS[role] || '')}</span>
|
|
3250
3281
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:0.6rem;">
|
|
3251
3282
|
<strong style="color:#e6edf3;font-size:0.95rem;">${roleIcon} ${escapeHtml((userProfile.agent_names && userProfile.agent_names[role]) || role.charAt(0).toUpperCase() + role.slice(1))}</strong>
|
|
@@ -3254,6 +3285,7 @@ function renderAgentUI(data) {
|
|
|
3254
3285
|
<div style="font-size:0.75rem;color:#8b949e;line-height:1.6;">
|
|
3255
3286
|
<div style="margin-top:0.3rem;color:#c9d1d9;font-size:0.72rem;max-height:3em;overflow:hidden;text-overflow:ellipsis;">${escapeHtml((a.last_output || '').substring(0, 150))}</div>
|
|
3256
3287
|
</div>
|
|
3288
|
+
${elapsedHtml}
|
|
3257
3289
|
${contextHtml}
|
|
3258
3290
|
</div>`;
|
|
3259
3291
|
}).join('');
|
|
@@ -3276,6 +3308,18 @@ function renderAgentUI(data) {
|
|
|
3276
3308
|
busyPhraseIntervals[role] = null;
|
|
3277
3309
|
busyPhraseIndices[role] = 0;
|
|
3278
3310
|
}
|
|
3311
|
+
if (a.status === 'busy' && a.started_at && !elapsedIntervals[role]) {
|
|
3312
|
+
const startMs = new Date(a.started_at).getTime();
|
|
3313
|
+
elapsedIntervals[role] = setInterval(() => {
|
|
3314
|
+
const el = document.getElementById('agent-elapsed-' + role);
|
|
3315
|
+
if (!el) return;
|
|
3316
|
+
const secs = Math.max(0, Math.floor((Date.now() - startMs) / 1000));
|
|
3317
|
+
el.textContent = 'Running: ' + Math.floor(secs / 60) + 'm ' + (secs % 60) + 's';
|
|
3318
|
+
}, 1000);
|
|
3319
|
+
} else if (a.status !== 'busy' && elapsedIntervals[role]) {
|
|
3320
|
+
clearInterval(elapsedIntervals[role]);
|
|
3321
|
+
elapsedIntervals[role] = null;
|
|
3322
|
+
}
|
|
3279
3323
|
});
|
|
3280
3324
|
|
|
3281
3325
|
renderMessageLog(document.getElementById('agent-messages'), messages);
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{memstack_skill_loader-4.0.1 → memstack_skill_loader-4.0.3}/src/memstack_skill_loader/__main__.py
RENAMED
|
File without changes
|
{memstack_skill_loader-4.0.1 → memstack_skill_loader-4.0.3}/src/memstack_skill_loader/categories.py
RENAMED
|
File without changes
|
{memstack_skill_loader-4.0.1 → memstack_skill_loader-4.0.3}/src/memstack_skill_loader/compression.py
RENAMED
|
File without changes
|
{memstack_skill_loader-4.0.1 → memstack_skill_loader-4.0.3}/src/memstack_skill_loader/config.py
RENAMED
|
File without changes
|
{memstack_skill_loader-4.0.1 → memstack_skill_loader-4.0.3}/src/memstack_skill_loader/dashboard.py
RENAMED
|
File without changes
|
{memstack_skill_loader-4.0.1 → memstack_skill_loader-4.0.3}/src/memstack_skill_loader/indexer.py
RENAMED
|
File without changes
|
{memstack_skill_loader-4.0.1 → memstack_skill_loader-4.0.3}/src/memstack_skill_loader/license.py
RENAMED
|
File without changes
|
{memstack_skill_loader-4.0.1 → memstack_skill_loader-4.0.3}/src/memstack_skill_loader/memory_db.py
RENAMED
|
File without changes
|
{memstack_skill_loader-4.0.1 → memstack_skill_loader-4.0.3}/src/memstack_skill_loader/search.py
RENAMED
|
File without changes
|
{memstack_skill_loader-4.0.1 → memstack_skill_loader-4.0.3}/src/memstack_skill_loader/server.py
RENAMED
|
File without changes
|
|
File without changes
|
{memstack_skill_loader-4.0.1 → memstack_skill_loader-4.0.3}/src/memstack_skill_loader/stats.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|