memstack-skill-loader 4.0.0__tar.gz → 4.0.2__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.0/src/memstack_skill_loader.egg-info → memstack_skill_loader-4.0.2}/PKG-INFO +1 -1
- {memstack_skill_loader-4.0.0 → memstack_skill_loader-4.0.2}/pyproject.toml +1 -1
- {memstack_skill_loader-4.0.0 → memstack_skill_loader-4.0.2}/src/memstack_skill_loader/__init__.py +1 -1
- {memstack_skill_loader-4.0.0 → memstack_skill_loader-4.0.2}/src/memstack_skill_loader/agent_runner.py +104 -29
- {memstack_skill_loader-4.0.0 → memstack_skill_loader-4.0.2}/src/memstack_skill_loader/config.py +2 -2
- {memstack_skill_loader-4.0.0 → memstack_skill_loader-4.0.2}/src/memstack_skill_loader/dashboard.html +42 -6
- {memstack_skill_loader-4.0.0 → memstack_skill_loader-4.0.2/src/memstack_skill_loader.egg-info}/PKG-INFO +1 -1
- {memstack_skill_loader-4.0.0 → memstack_skill_loader-4.0.2}/MANIFEST.in +0 -0
- {memstack_skill_loader-4.0.0 → memstack_skill_loader-4.0.2}/README.md +0 -0
- {memstack_skill_loader-4.0.0 → memstack_skill_loader-4.0.2}/setup.cfg +0 -0
- {memstack_skill_loader-4.0.0 → memstack_skill_loader-4.0.2}/src/memstack_skill_loader/__main__.py +0 -0
- {memstack_skill_loader-4.0.0 → memstack_skill_loader-4.0.2}/src/memstack_skill_loader/categories.py +0 -0
- {memstack_skill_loader-4.0.0 → memstack_skill_loader-4.0.2}/src/memstack_skill_loader/compression.py +0 -0
- {memstack_skill_loader-4.0.0 → memstack_skill_loader-4.0.2}/src/memstack_skill_loader/dashboard.py +0 -0
- {memstack_skill_loader-4.0.0 → memstack_skill_loader-4.0.2}/src/memstack_skill_loader/indexer.py +0 -0
- {memstack_skill_loader-4.0.0 → memstack_skill_loader-4.0.2}/src/memstack_skill_loader/license.py +0 -0
- {memstack_skill_loader-4.0.0 → memstack_skill_loader-4.0.2}/src/memstack_skill_loader/memory_db.py +0 -0
- {memstack_skill_loader-4.0.0 → memstack_skill_loader-4.0.2}/src/memstack_skill_loader/search.py +0 -0
- {memstack_skill_loader-4.0.0 → memstack_skill_loader-4.0.2}/src/memstack_skill_loader/server.py +0 -0
- {memstack_skill_loader-4.0.0 → memstack_skill_loader-4.0.2}/src/memstack_skill_loader/skill_config.py +0 -0
- {memstack_skill_loader-4.0.0 → memstack_skill_loader-4.0.2}/src/memstack_skill_loader/stats.py +0 -0
- {memstack_skill_loader-4.0.0 → memstack_skill_loader-4.0.2}/src/memstack_skill_loader/tfidf_search.py +0 -0
- {memstack_skill_loader-4.0.0 → memstack_skill_loader-4.0.2}/src/memstack_skill_loader/version_check.py +0 -0
- {memstack_skill_loader-4.0.0 → memstack_skill_loader-4.0.2}/src/memstack_skill_loader.egg-info/SOURCES.txt +0 -0
- {memstack_skill_loader-4.0.0 → memstack_skill_loader-4.0.2}/src/memstack_skill_loader.egg-info/dependency_links.txt +0 -0
- {memstack_skill_loader-4.0.0 → memstack_skill_loader-4.0.2}/src/memstack_skill_loader.egg-info/entry_points.txt +0 -0
- {memstack_skill_loader-4.0.0 → memstack_skill_loader-4.0.2}/src/memstack_skill_loader.egg-info/requires.txt +0 -0
- {memstack_skill_loader-4.0.0 → memstack_skill_loader-4.0.2}/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.0 → memstack_skill_loader-4.0.2}/src/memstack_skill_loader/config.py
RENAMED
|
@@ -158,8 +158,8 @@ def load_config(config_path: Path | None = None) -> Config:
|
|
|
158
158
|
_config_dir=config_path.parent.resolve(),
|
|
159
159
|
)
|
|
160
160
|
|
|
161
|
-
# Auto-detect pro-skills if license key is set
|
|
162
|
-
if os.environ.get("MEMSTACK_PRO_LICENSE_KEY"):
|
|
161
|
+
# Auto-detect pro-skills if license key is set (env var or license.json)
|
|
162
|
+
if os.environ.get("MEMSTACK_PRO_LICENSE_KEY") or (Path.home() / ".memstack" / "license.json").exists():
|
|
163
163
|
config = config.with_pro_skills()
|
|
164
164
|
|
|
165
165
|
return config
|
{memstack_skill_loader-4.0.0 → memstack_skill_loader-4.0.2}/src/memstack_skill_loader/dashboard.html
RENAMED
|
@@ -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
|
|
|
@@ -3111,10 +3113,17 @@ function renderAgentUI(data) {
|
|
|
3111
3113
|
launcher.style.display = 'none';
|
|
3112
3114
|
active.style.display = 'none';
|
|
3113
3115
|
completed.style.display = '';
|
|
3114
|
-
const
|
|
3116
|
+
const hasTimeout = Object.values(agents).some(ag => ag.status === 'timeout');
|
|
3117
|
+
const hasCrash = Object.values(agents).some(ag => ag.status === 'crashed');
|
|
3118
|
+
let icon, title;
|
|
3119
|
+
if (status === 'completed') { icon = '✅'; title = 'Task Completed'; }
|
|
3120
|
+
else if (status === 'stopped') { icon = '⏹'; title = 'Task Stopped'; }
|
|
3121
|
+
else if (hasTimeout) { icon = '⏳'; title = 'Task Timed Out'; }
|
|
3122
|
+
else if (hasCrash) { icon = '⚠️'; title = 'Task Failed'; }
|
|
3123
|
+
else { icon = '⚠️'; title = 'Task Failed'; }
|
|
3115
3124
|
document.querySelector('#agent-completed div div:first-child').innerHTML = icon;
|
|
3116
3125
|
const h2 = document.querySelector('#agent-completed h2');
|
|
3117
|
-
if (h2) h2.textContent =
|
|
3126
|
+
if (h2) h2.textContent = title;
|
|
3118
3127
|
document.getElementById('agent-result-summary').textContent = status === 'stopped' ? (data.result || 'Task stopped by user') : (data.result || `Session ${status}`);
|
|
3119
3128
|
const COMPACTION_LIMIT = 200000;
|
|
3120
3129
|
const COMPACTION_WARN = 155000;
|
|
@@ -3218,8 +3227,12 @@ function renderAgentUI(data) {
|
|
|
3218
3227
|
const phrase = BUSY_PHRASES[role][busyPhraseIndices[role] % BUSY_PHRASES[role].length];
|
|
3219
3228
|
const customName = (userProfile.agent_names && userProfile.agent_names[role]) || role.charAt(0).toUpperCase() + role.slice(1);
|
|
3220
3229
|
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
|
-
|
|
3230
|
+
} else if (a.status === 'timeout') {
|
|
3231
|
+
statusDisplay = 'Timed out';
|
|
3232
|
+
} else if (a.status === 'crashed') {
|
|
3233
|
+
statusDisplay = 'Crashed';
|
|
3234
|
+
} else if ((a.status === 'done' || a.status === 'completed') && (a.started_at || data.started_at)) {
|
|
3235
|
+
const startMs = new Date(a.started_at || data.started_at).getTime();
|
|
3223
3236
|
const elapsed = Math.max(0, Math.round((Date.now() - startMs) / 1000));
|
|
3224
3237
|
const mins = Math.floor(elapsed / 60);
|
|
3225
3238
|
const secs = elapsed % 60;
|
|
@@ -3227,6 +3240,15 @@ function renderAgentUI(data) {
|
|
|
3227
3240
|
} else {
|
|
3228
3241
|
statusDisplay = a.status;
|
|
3229
3242
|
}
|
|
3243
|
+
let elapsedHtml = '';
|
|
3244
|
+
if (a.status === 'busy' && a.started_at) {
|
|
3245
|
+
const secs = Math.max(0, Math.floor((Date.now() - new Date(a.started_at).getTime()) / 1000));
|
|
3246
|
+
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>`;
|
|
3247
|
+
} else if ((a.status === 'done' || a.status === 'completed' || a.status === 'timeout' || a.status === 'crashed') && a.started_at) {
|
|
3248
|
+
const secs = Math.max(0, Math.floor((Date.now() - new Date(a.started_at).getTime()) / 1000));
|
|
3249
|
+
const label = a.status === 'timeout' ? 'Timed out after' : a.status === 'crashed' ? 'Crashed after' : 'Completed in';
|
|
3250
|
+
elapsedHtml = `<div style="font-size:0.7rem;color:#8b949e;margin-top:0.3rem;">${label} ${Math.floor(secs/60)}m ${secs%60}s</div>`;
|
|
3251
|
+
}
|
|
3230
3252
|
const COMPACTION_LIMIT = 200000;
|
|
3231
3253
|
const tokens = (a.context_tokens || 0);
|
|
3232
3254
|
const pct = tokens > 0 ? Math.min(100, Math.round(tokens / COMPACTION_LIMIT * 100)) : -1;
|
|
@@ -3245,7 +3267,8 @@ function renderAgentUI(data) {
|
|
|
3245
3267
|
showToast(role + ' context at ' + pct + '% — diary auto-saved', 'warning', 5000);
|
|
3246
3268
|
}
|
|
3247
3269
|
}
|
|
3248
|
-
|
|
3270
|
+
const errorBorder = (a.status === 'error' || a.status === 'timeout' || a.status === 'crashed') ? ' agent-card-error-border' : '';
|
|
3271
|
+
return `<div class="agent-card${errorBorder}" style="${pulseStyle}">
|
|
3249
3272
|
<span class="agent-tooltip">${escapeHtml(ROLE_DESCRIPTIONS[role] || '')}</span>
|
|
3250
3273
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:0.6rem;">
|
|
3251
3274
|
<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 +3277,7 @@ function renderAgentUI(data) {
|
|
|
3254
3277
|
<div style="font-size:0.75rem;color:#8b949e;line-height:1.6;">
|
|
3255
3278
|
<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
3279
|
</div>
|
|
3280
|
+
${elapsedHtml}
|
|
3257
3281
|
${contextHtml}
|
|
3258
3282
|
</div>`;
|
|
3259
3283
|
}).join('');
|
|
@@ -3276,6 +3300,18 @@ function renderAgentUI(data) {
|
|
|
3276
3300
|
busyPhraseIntervals[role] = null;
|
|
3277
3301
|
busyPhraseIndices[role] = 0;
|
|
3278
3302
|
}
|
|
3303
|
+
if (a.status === 'busy' && a.started_at && !elapsedIntervals[role]) {
|
|
3304
|
+
const startMs = new Date(a.started_at).getTime();
|
|
3305
|
+
elapsedIntervals[role] = setInterval(() => {
|
|
3306
|
+
const el = document.getElementById('agent-elapsed-' + role);
|
|
3307
|
+
if (!el) return;
|
|
3308
|
+
const secs = Math.max(0, Math.floor((Date.now() - startMs) / 1000));
|
|
3309
|
+
el.textContent = 'Running: ' + Math.floor(secs / 60) + 'm ' + (secs % 60) + 's';
|
|
3310
|
+
}, 1000);
|
|
3311
|
+
} else if (a.status !== 'busy' && elapsedIntervals[role]) {
|
|
3312
|
+
clearInterval(elapsedIntervals[role]);
|
|
3313
|
+
elapsedIntervals[role] = null;
|
|
3314
|
+
}
|
|
3279
3315
|
});
|
|
3280
3316
|
|
|
3281
3317
|
renderMessageLog(document.getElementById('agent-messages'), messages);
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{memstack_skill_loader-4.0.0 → memstack_skill_loader-4.0.2}/src/memstack_skill_loader/__main__.py
RENAMED
|
File without changes
|
{memstack_skill_loader-4.0.0 → memstack_skill_loader-4.0.2}/src/memstack_skill_loader/categories.py
RENAMED
|
File without changes
|
{memstack_skill_loader-4.0.0 → memstack_skill_loader-4.0.2}/src/memstack_skill_loader/compression.py
RENAMED
|
File without changes
|
{memstack_skill_loader-4.0.0 → memstack_skill_loader-4.0.2}/src/memstack_skill_loader/dashboard.py
RENAMED
|
File without changes
|
{memstack_skill_loader-4.0.0 → memstack_skill_loader-4.0.2}/src/memstack_skill_loader/indexer.py
RENAMED
|
File without changes
|
{memstack_skill_loader-4.0.0 → memstack_skill_loader-4.0.2}/src/memstack_skill_loader/license.py
RENAMED
|
File without changes
|
{memstack_skill_loader-4.0.0 → memstack_skill_loader-4.0.2}/src/memstack_skill_loader/memory_db.py
RENAMED
|
File without changes
|
{memstack_skill_loader-4.0.0 → memstack_skill_loader-4.0.2}/src/memstack_skill_loader/search.py
RENAMED
|
File without changes
|
{memstack_skill_loader-4.0.0 → memstack_skill_loader-4.0.2}/src/memstack_skill_loader/server.py
RENAMED
|
File without changes
|
|
File without changes
|
{memstack_skill_loader-4.0.0 → memstack_skill_loader-4.0.2}/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
|