loki-mode 7.104.4 → 7.105.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/SKILL.md CHANGED
@@ -3,7 +3,7 @@ name: loki-mode
3
3
  description: Autonomous spec-driven build system with a built-in trust layer. It does not call work done until it is verified (RARV-C closure loop, 8 quality gates, completion council, verified-completion evidence gate). Triggers on "Loki Mode". Takes a spec (PRD, GitHub issue, OpenAPI doc, etc.) to deployed product with minimal human intervention. Provider-agnostic. Requires --dangerously-skip-permissions flag.
4
4
  ---
5
5
 
6
- # Loki Mode v7.104.4
6
+ # Loki Mode v7.105.0
7
7
 
8
8
  **You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
9
9
 
@@ -408,4 +408,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
408
408
 
409
409
  ---
410
410
 
411
- **v7.104.4 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
411
+ **v7.105.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 7.104.4
1
+ 7.105.0
@@ -3226,10 +3226,36 @@ council_should_stop() {
3226
3226
  circuit_triggered=true
3227
3227
  fi
3228
3228
 
3229
- # Only run council at check intervals OR if circuit breaker triggered
3229
+ # Only run council at check intervals OR if circuit breaker triggered OR the
3230
+ # agent has EXPLICITLY claimed completion this iteration (v7.105.0 convergence
3231
+ # fix). Rationale, measured from real build telemetry (docs/SPEED-DIAGNOSIS):
3232
+ # a build could be verifiably done at iteration 1 (reviews non-blocking, tests
3233
+ # green, checklist complete) yet the council was not even allowed to evaluate
3234
+ # until the next 5-iteration boundary -- so the loop ground out "next
3235
+ # improvement" iterations the user never asked for (14 iterations for a job
3236
+ # done in ~1). An explicit completion CLAIM is the strongest possible signal
3237
+ # that it is worth asking the council NOW. This changes only WHEN the council
3238
+ # evaluates, never WHETHER it must approve: MIN_ITERATIONS below, the hard
3239
+ # checklist gate, the evidence gate, the aggregate vote, and the devil's
3240
+ # advocate all still run unchanged. A premature or unearned claim is still
3241
+ # rejected (returns 1, loop continues); it is never a forced green.
3242
+ #
3243
+ # The claim signal is the structured one (loki_complete_task MCP tool ->
3244
+ # .loki/signals/COMPLETION_REQUESTED, or the runner exporting
3245
+ # LOKI_COMPLETION_CLAIMED=1 for this iteration), NOT the fuzzy log-grep the
3246
+ # circuit breaker uses -- so it fires on a real, intentional claim only.
3247
+ local completion_claimed=false
3248
+ if [ "${LOKI_COMPLETION_CLAIMED:-0}" = "1" ] \
3249
+ || [ -f "${TARGET_DIR:-.}/.loki/signals/COMPLETION_REQUESTED" ]; then
3250
+ completion_claimed=true
3251
+ fi
3252
+
3230
3253
  local should_check=false
3231
3254
  if [ "$circuit_triggered" = "true" ]; then
3232
3255
  should_check=true
3256
+ elif [ "$completion_claimed" = "true" ]; then
3257
+ should_check=true
3258
+ log_info "[Council] Completion claimed this iteration -- evaluating now (not deferring to the ${COUNCIL_CHECK_INTERVAL}-iteration interval)"
3233
3259
  elif [ $((ITERATION_COUNT % COUNCIL_CHECK_INTERVAL)) -eq 0 ]; then
3234
3260
  should_check=true
3235
3261
  fi
package/autonomy/run.sh CHANGED
@@ -17530,7 +17530,30 @@ else:
17530
17530
  if type ensure_completion_test_evidence &>/dev/null; then
17531
17531
  ensure_completion_test_evidence || true
17532
17532
  fi
17533
- if type council_should_stop &>/dev/null && council_should_stop; then
17533
+ # v7.105.0 convergence: if the agent EXPLICITLY claimed completion this
17534
+ # iteration (structured loki_complete_task / COMPLETION_REQUESTED
17535
+ # signal), tell the council to evaluate NOW instead of deferring to the
17536
+ # 5-iteration check interval. The council still must approve (MIN_
17537
+ # ITERATIONS + hard checklist + evidence gate + vote + devil's advocate
17538
+ # all unchanged); this only stops an already-verified build from
17539
+ # grinding needless "next improvement" iterations. Scoped to this call.
17540
+ #
17541
+ # CRITICAL (council-review finding): we must PEEK at the claim signal
17542
+ # NON-DESTRUCTIVELY. check_task_completion_signal() CONSUMES the signal
17543
+ # (rm -f on read), and the DEFAULT completion-promise route below
17544
+ # (v6.82.0, the live gate-based acceptance path) also consumes it -- so
17545
+ # calling the consuming detector here would drop the claim before that
17546
+ # route sees it, re-introducing the v7.28 "claim drop" bug. Instead we
17547
+ # only test the signal FILES' existence (the same two paths
17548
+ # check_task_completion_signal reads), leaving consumption to the
17549
+ # existing single owner. The council side likewise peeks
17550
+ # ($TARGET_DIR/.loki/signals/COMPLETION_REQUESTED) without consuming.
17551
+ local _loki_completion_claimed=0
17552
+ if [ -f "${TARGET_DIR:-.}/.loki/signals/TASK_COMPLETION_CLAIMED" ] \
17553
+ || [ -f "${TARGET_DIR:-.}/.loki/signals/COMPLETION_REQUESTED" ]; then
17554
+ _loki_completion_claimed=1
17555
+ fi
17556
+ if type council_should_stop &>/dev/null && LOKI_COMPLETION_CLAIMED="$_loki_completion_claimed" council_should_stop; then
17534
17557
  # bash-F1: council_should_stop returns 0 from a genuine approval
17535
17558
  # AND from two force-stop safety valves (stagnation flood /
17536
17559
  # repeated done-signals). A force-stop is NOT a verified-complete
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "7.104.4"
10
+ __version__ = "7.105.0"
11
11
 
12
12
  # Expose the control app for easy import
13
13
  try:
@@ -5007,41 +5007,79 @@ def _sanitize_agent_id(agent_id: str) -> str:
5007
5007
  )
5008
5008
  return agent_id
5009
5009
 
5010
+
5011
+ # --- Memory file enumeration (single source of truth) ------------------------
5012
+ # v7.104.5 (PR #178 follow-up): the memory store writes episodes and skills into
5013
+ # DATED subdirectories (episodic/YYYY-MM-DD/*.json, skills/*/*.json), so a flat
5014
+ # .glob("*.json") finds nothing and every endpoint that used it under-reported.
5015
+ # PR #178 fixed only /api/memory/summary; the sibling /api/memory/episodes and
5016
+ # the skills listings still used a flat glob, so the count and the drill-in list
5017
+ # disagreed (summary said 28, the list returned 0) -- a trust-eroding
5018
+ # inconsistency. These two helpers are the ONE way to enumerate memory files, so
5019
+ # counts and lists can never diverge again. Both recurse and skip .lock files.
5020
+ def _memory_episode_files(ep_dir) -> list:
5021
+ """All episode JSON files under episodic/ (recursing dated subdirs), sorted."""
5022
+ try:
5023
+ return sorted(p for p in ep_dir.rglob("*.json") if not p.name.endswith(".lock"))
5024
+ except Exception:
5025
+ return []
5026
+
5027
+
5028
+ def _memory_skill_files(skills_dir) -> list:
5029
+ """All skill JSON files under skills/ (recursing subdirs), sorted."""
5030
+ try:
5031
+ return sorted(p for p in skills_dir.rglob("*.json") if not p.name.endswith(".lock"))
5032
+ except Exception:
5033
+ return []
5034
+
5035
+
5010
5036
  @app.get("/api/memory/summary", dependencies=[Depends(auth.require_scope("read"))])
5011
5037
  async def get_memory_summary():
5012
5038
  """Get memory system summary from .loki/memory/."""
5013
- # Try SQLite backend first for accurate counts
5039
+ # Try SQLite backend first for accurate counts.
5040
+ # NOTE (PR #178, @iizotov): _get_memory_storage() returns a SQLiteMemoryStorage
5041
+ # object whenever the module imports, even if no .db file exists yet. On a
5042
+ # JSON-backed project that yields all-zero stats, so we must NOT return here on
5043
+ # empty counts -- otherwise the JSON file fallback below never runs and the
5044
+ # dashboard shows 0 episodes/patterns/skills even though they exist on disk.
5045
+ # Only trust SQLite when it actually reports data; else fall through to JSON.
5014
5046
  storage = _get_memory_storage()
5015
5047
  if storage is not None:
5016
5048
  try:
5017
5049
  stats = storage.get_stats()
5018
- summary = {
5019
- "episodic": {"count": stats.get("episode_count", 0), "latestDate": None},
5020
- "semantic": {"patterns": stats.get("pattern_count", 0), "antiPatterns": 0},
5021
- "procedural": {"skills": stats.get("skill_count", 0)},
5022
- "backend": "sqlite",
5023
- }
5024
- # Get latest episode date
5025
- episode_ids = storage.list_episodes(limit=1)
5026
- if episode_ids:
5027
- ep = storage.load_episode(episode_ids[0])
5028
- if ep:
5029
- summary["episodic"]["latestDate"] = ep.get("timestamp", "")
5030
- # Token economics from JSON (not in SQLite)
5031
- econ_file = _get_loki_dir() / "memory" / "token_economics.json"
5032
- if econ_file.exists():
5033
- try:
5034
- econ = json.loads(econ_file.read_text())
5035
- summary["tokenEconomics"] = {
5036
- "discoveryTokens": econ.get("discoveryTokens", 0),
5037
- "readTokens": econ.get("readTokens", 0),
5038
- "savingsPercent": econ.get("savingsPercent", 0),
5039
- }
5040
- except Exception:
5050
+ total = (
5051
+ stats.get("episode_count", 0)
5052
+ + stats.get("pattern_count", 0)
5053
+ + stats.get("skill_count", 0)
5054
+ )
5055
+ if total > 0:
5056
+ summary = {
5057
+ "episodic": {"count": stats.get("episode_count", 0), "latestDate": None},
5058
+ "semantic": {"patterns": stats.get("pattern_count", 0), "antiPatterns": 0},
5059
+ "procedural": {"skills": stats.get("skill_count", 0)},
5060
+ "backend": "sqlite",
5061
+ }
5062
+ # Get latest episode date
5063
+ episode_ids = storage.list_episodes(limit=1)
5064
+ if episode_ids:
5065
+ ep = storage.load_episode(episode_ids[0])
5066
+ if ep:
5067
+ summary["episodic"]["latestDate"] = ep.get("timestamp", "")
5068
+ # Token economics from JSON (not in SQLite)
5069
+ econ_file = _get_loki_dir() / "memory" / "token_economics.json"
5070
+ if econ_file.exists():
5071
+ try:
5072
+ econ = json.loads(econ_file.read_text())
5073
+ summary["tokenEconomics"] = {
5074
+ "discoveryTokens": econ.get("discoveryTokens", 0),
5075
+ "readTokens": econ.get("readTokens", 0),
5076
+ "savingsPercent": econ.get("savingsPercent", 0),
5077
+ }
5078
+ except Exception:
5079
+ summary["tokenEconomics"] = {"discoveryTokens": 0, "readTokens": 0, "savingsPercent": 0}
5080
+ else:
5041
5081
  summary["tokenEconomics"] = {"discoveryTokens": 0, "readTokens": 0, "savingsPercent": 0}
5042
- else:
5043
- summary["tokenEconomics"] = {"discoveryTokens": 0, "readTokens": 0, "savingsPercent": 0}
5044
- return summary
5082
+ return summary
5045
5083
  except Exception:
5046
5084
  pass
5047
5085
 
@@ -5057,14 +5095,28 @@ async def get_memory_summary():
5057
5095
 
5058
5096
  ep_dir = memory_dir / "episodic"
5059
5097
  if ep_dir.exists():
5060
- episodes = sorted(ep_dir.glob("*.json"))
5098
+ # Episodes are stored under dated subdirectories (episodic/YYYY-MM-DD/*.json),
5099
+ # so a flat glob("*.json") finds nothing (PR #178, @iizotov). Recurse and
5100
+ # ignore .lock files. Shared with /api/memory/episodes via _memory_episode_
5101
+ # files() so the summary count and the drill-in list can never disagree.
5102
+ episodes = _memory_episode_files(ep_dir)
5061
5103
  summary["episodic"]["count"] = len(episodes)
5062
5104
  if episodes:
5063
- try:
5064
- latest = json.loads(episodes[-1].read_text())
5065
- summary["episodic"]["latestDate"] = latest.get("timestamp", "")
5066
- except Exception:
5067
- pass
5105
+ # latestDate must be the newest by RECORDED TIMESTAMP, not by filename
5106
+ # sort: intra-day filenames carry a hash tiebreak, not a time, so a
5107
+ # filename sort can report the wrong episode (observed ~15h off). Pick
5108
+ # the max parsed timestamp; fall back to the last file only if no
5109
+ # timestamp is readable, so an honest value is preferred over a
5110
+ # confidently-wrong one.
5111
+ latest_ts = ""
5112
+ for _epf in episodes:
5113
+ try:
5114
+ _ts = json.loads(_epf.read_text()).get("timestamp", "")
5115
+ if isinstance(_ts, str) and _ts > latest_ts:
5116
+ latest_ts = _ts
5117
+ except Exception:
5118
+ continue
5119
+ summary["episodic"]["latestDate"] = latest_ts or None
5068
5120
 
5069
5121
  sem_dir = memory_dir / "semantic"
5070
5122
  patterns_file = sem_dir / "patterns.json"
@@ -5084,7 +5136,7 @@ async def get_memory_summary():
5084
5136
 
5085
5137
  skills_dir = memory_dir / "skills"
5086
5138
  if skills_dir.exists():
5087
- summary["procedural"]["skills"] = len(list(skills_dir.glob("*.json")))
5139
+ summary["procedural"]["skills"] = len(_memory_skill_files(skills_dir))
5088
5140
 
5089
5141
  econ_file = memory_dir / "token_economics.json"
5090
5142
  if econ_file.exists():
@@ -5122,20 +5174,22 @@ async def list_episodes(limit: int = Query(default=50, ge=1, le=1000)):
5122
5174
  except Exception:
5123
5175
  pass
5124
5176
 
5125
- # Fallback to JSON files -- use heapq to avoid sorting all files
5126
- import heapq
5177
+ # Fallback to JSON files. Enumerate via the shared helper (recurses the
5178
+ # dated subdirs) so this list agrees with /api/memory/summary's count --
5179
+ # the flat glob here was the exact parity gap PR #178's review flagged
5180
+ # (summary said 28, this returned 0). Read each file, then return the
5181
+ # newest `limit` by RECORDED timestamp (not filename, whose intra-day
5182
+ # tiebreak is a hash, not a time).
5127
5183
  ep_dir = _get_loki_dir() / "memory" / "episodic"
5128
- episodes = []
5184
+ loaded = []
5129
5185
  if ep_dir.exists():
5130
- all_files = ep_dir.glob("*.json")
5131
- # nlargest by filename (timestamps sort lexicographically) avoids full sort
5132
- files = heapq.nlargest(limit, all_files, key=lambda f: f.name)
5133
- for f in files:
5186
+ for f in _memory_episode_files(ep_dir):
5134
5187
  try:
5135
- episodes.append(json.loads(f.read_text()))
5188
+ loaded.append(json.loads(f.read_text()))
5136
5189
  except Exception:
5137
5190
  pass
5138
- return episodes
5191
+ loaded.sort(key=lambda e: e.get("timestamp", "") if isinstance(e, dict) else "", reverse=True)
5192
+ return loaded[:limit]
5139
5193
 
5140
5194
  return await asyncio.to_thread(_load_episodes)
5141
5195
 
@@ -5159,7 +5213,7 @@ async def get_episode(episode_id: str):
5159
5213
  if not ep_dir.exists():
5160
5214
  raise HTTPException(status_code=404, detail="Episode not found")
5161
5215
  real_loki = os.path.realpath(str(loki_dir))
5162
- for f in ep_dir.glob("*.json"):
5216
+ for f in _memory_episode_files(ep_dir):
5163
5217
  resolved = os.path.realpath(f)
5164
5218
  if not resolved.startswith(real_loki + os.sep) and resolved != real_loki:
5165
5219
  raise HTTPException(status_code=403, detail="Access denied")
@@ -5235,7 +5289,7 @@ async def list_skills():
5235
5289
  skills_dir = _get_loki_dir() / "memory" / "skills"
5236
5290
  skills = []
5237
5291
  if skills_dir.exists():
5238
- for f in sorted(skills_dir.glob("*.json")):
5292
+ for f in _memory_skill_files(skills_dir):
5239
5293
  try:
5240
5294
  skills.append(json.loads(f.read_text()))
5241
5295
  except Exception:
@@ -5253,7 +5307,7 @@ async def get_skill(skill_id: str):
5253
5307
  if not skills_dir.exists():
5254
5308
  raise HTTPException(status_code=404, detail="Skill not found")
5255
5309
  real_loki = os.path.realpath(str(loki_dir))
5256
- for f in skills_dir.glob("*.json"):
5310
+ for f in _memory_skill_files(skills_dir):
5257
5311
  resolved = os.path.realpath(f)
5258
5312
  if not resolved.startswith(real_loki + os.sep) and resolved != real_loki:
5259
5313
  raise HTTPException(status_code=403, detail="Access denied")
@@ -5699,11 +5753,7 @@ async def get_memory_stats():
5699
5753
  ep_count = 0
5700
5754
  ep_dir = memory_dir / "episodic"
5701
5755
  if ep_dir.exists():
5702
- for d in ep_dir.iterdir():
5703
- if d.is_dir():
5704
- ep_count += len(list(d.glob("*.json")))
5705
- elif d.suffix == ".json":
5706
- ep_count += 1
5756
+ ep_count = len(_memory_episode_files(ep_dir))
5707
5757
 
5708
5758
  pat_count = 0
5709
5759
  patterns_file = memory_dir / "semantic" / "patterns.json"
@@ -5717,7 +5767,7 @@ async def get_memory_stats():
5717
5767
  skill_count = 0
5718
5768
  skills_dir = memory_dir / "skills"
5719
5769
  if skills_dir.exists():
5720
- skill_count = len(list(skills_dir.glob("*.json")))
5770
+ skill_count = len(_memory_skill_files(skills_dir))
5721
5771
 
5722
5772
  return {
5723
5773
  "backend": "json",
@@ -0,0 +1,106 @@
1
+ # Founder Status + Decision List (2026-07-01)
2
+
3
+ The consolidated "where everything stands + the decisions only you can make" report.
4
+ Written after: 5 patch releases shipped, top-100 backlog enumerated + triaged, ecosystem
5
+ currency assessed, no-HITL product principle recorded.
6
+
7
+ ## 1. Shipped this session (all live on npm/Docker/GH, council-gated)
8
+
9
+ | Version | What | Verified |
10
+ |---|---|---|
11
+ | v7.104.2 | Native/Keychain Claude login recognized; `loki start` no longer false-blocks a logged-in user; `loki doctor` confirms login (bash+Bun parity) | reproduced + 8-case test + live Docker |
12
+ | v7.104.3 | Task-list accuracy: honest done column, no empty cards / dups / fake-green (failed iters honestly labeled) | live vs real anonima, browser screenshots, council 3/3 |
13
+ | v7.104.4 | `/api/tasks` never 500s on malformed dashboard-state.json | RED/GREEN-proven test |
14
+ | v7.104.5 | Memory panel correct + self-consistent on JSON-backed projects (completes PR #178, @iizotov co-authored, PR closed with thank-you) | live vs anonima, parity tests |
15
+ | (earlier) v7.104.2 auth + v7.104.3 task-list | (same as above; two-patch train) | |
16
+
17
+ Also: closed issue #177 (spam advisory). PR #178 merged-in-substance + contributor thanked.
18
+
19
+ ## 2. Backlog reality (honest)
20
+
21
+ The top-100 was enumerated (116 raw -> 100 ranked) and then TRIAGED against current source.
22
+ Key finding: **the autonomous critical/high queue is essentially ALREADY DONE.**
23
+
24
+ | Rank | Item | Triage verdict |
25
+ |---|---|---|
26
+ | 1 | run.sh self-delete EXIT trap | already fixed (guarded to /tmp/loki-run-*; verified safe) |
27
+ | 2 | saas cross-tenant leak + stuck-Building | CLOSED (commits a7667d0/44f7c29/8b05b36; 187/187 tests) |
28
+ | 9 | anti-fake-green + gate parity | shipped (verify 282 tests; coherentStatus guard) |
29
+ | 10 | VS-7 worker cutover | CLOSED (commit e6b96ed; BFF 181 + worker 102 tests) |
30
+ | 17 | base_unreachable suppresses empty_diff | already wired (completion-council.sh:1616) |
31
+ | 22 | 3 parallel reviewers | already implemented (run.sh:10461-10954) |
32
+
33
+ The autonomous well is mostly dry BECAUSE the real remaining leverage is founder-gated
34
+ (the trust-as-product / enterprise surface). That is the honest headline: the next 10x is
35
+ not another patch, it is the decisions below.
36
+
37
+ ## 3. Ecosystem currency (what loki CONSUMES from the Claude ecosystem)
38
+
39
+ CLI invocation contract (claude 2.1.198): STABLE, no drift. Auth: fixed this session.
40
+ Model catalog: current (opus-4-8, sonnet-5, haiku-4-5, fable-5). Real deltas are small and
41
+ tracked in docs/ECOSYSTEM-CURRENCY-PLAN.json (EC-1..EC-4). Adjacent Anthropic PRODUCTS
42
+ (Managed Agents, Claude Tag, loop-engineering, agent identity) assessed as peer/inspiration
43
+ categories, NOT dependencies -- declined with reason (the CTO triage move).
44
+
45
+ The one real net-new build from this: **EC-1, the external-tool contract adapter + drift
46
+ test** -- the keychain incident this session proves it would catch a user-facing break
47
+ before a user does. Building autonomously now.
48
+
49
+ ## 4. DECISIONS ONLY YOU CAN MAKE (founder-gated, prepped to one-approval-away)
50
+
51
+ These are the real blockers to "easiest to integrate with anyone" + the trust-as-product moat.
52
+ Per the no-HITL principle, these are the LEGITIMATE exceptions: irreversible / strategic /
53
+ brand / legal / spend decisions a human must own. Each is ready for a one-word go.
54
+
55
+ ### A. Autonomi Verify productization (the trust-as-product core)
56
+ 1. **License decision (rank 5, S, BLOCKS everything commercial).** BUSL-1.1 vs open for
57
+ @autonomi/verify. No hosted/commercial launch until decided. DECISION: which license?
58
+ 2. **npm registry + package name (rank 4, M).** @autonomi/verify is currently unpublishable
59
+ (private:true, no exports/types/build, .ts bin). And a naming collision exists
60
+ (loki-vaas vs loki-verify bin vs `loki verify` subcommand, rank 13). DECISION: publish to
61
+ public npm as `@autonomi/verify`? Confirm the brand/bin name to resolve the collision.
62
+ -> Once decided, I can make it genuinely npm-installable + fix the collision autonomously.
63
+ 3. **Hosted /verify deploy (rank 6, M + rank 36/42 sandbox).** The signed neutral REST
64
+ endpoint is the category-defining asset but not deployed (Bun.serve-only, ephemeral key).
65
+ DECISION: deploy target (Fly/Render/AWS/your infra) + signing-key storage (secret manager)?
66
+ -> I can stage the Node entry + Dockerfile + serve script + 12-factor key guard to
67
+ one-command-deploy; I will NOT deploy or spend without your go.
68
+ 4. **wire verifyCompletion() pipeline (rank 3, XL).** The SDK/MCP verdict path is stubbed;
69
+ the whole value prop is unreachable until gate->integrity->council->cost->sign is wired.
70
+ Depends on (2)/(3) decisions. Large; I can start once the license + registry are set.
71
+
72
+ ### B. Enterprise scale (needed for any enterprise sale)
73
+ 5. **RBAC / SSO / API-key auth / metering (rank 15, XL)** + **on-prem persistence backend
74
+ (rank 16, L, Postgres/SQLite)** + **sandbox backend (rank 14, L, gVisor/Firecracker/Kata)**.
75
+ DECISION: which enterprise deploy shape do we target first (hosted multi-tenant vs on-prem)?
76
+ That choice sequences 14/15/16/36/42. -> I can build the PersistenceBackend interface +
77
+ RBAC scaffolding once you pick the shape.
78
+ 6. **GitHub App verification integration (rank 46, L, greenfield).** DECISION: build now or later?
79
+
80
+ ### C. Benchmark spend
81
+ 7. **SWE-bench Pro 119 resume (issue #174, paused 35/119).** Resume harness exists. DECISION:
82
+ spend the compute to finish? (This is a $ + time decision, hence gated.)
83
+
84
+ ### D. Outreach (rank/issue #172, low-risk but external-posting)
85
+ 8. MCP registry submission + Glama claim + awesome-list PRs. I can prepare the submissions;
86
+ the actual external POSTS need your nod (per action rules). DECISION: proceed with posts?
87
+
88
+ ## 5. What I am doing autonomously RIGHT NOW (no decision needed)
89
+ - EC-1 external-contract adapter + drift test (the one real net-new build).
90
+ - Small no-HITL audit: find every place the PRODUCT hard-stops/prompts a human; fix genuine
91
+ gaps so it decides from model+memory+cross-project context (degrading to honest-inconclusive,
92
+ never fake-green). Not a speculative refactor -- a triage + targeted fixes.
93
+ - NOT building: dashboard steering (#44) -- it is a human-in-the-loop feature, deprioritized by
94
+ the no-HITL mandate; architect plan is durable and waits. NOT building "autonomous-everywhere"
95
+ as a speculative refactor.
96
+
97
+ ## 6. The one-line asks
98
+ Reply with any subset:
99
+ - **License:** BUSL / MIT / Apache / other for @autonomi/verify
100
+ - **npm:** publish `@autonomi/verify` publicly? + confirm bin name (resolve loki-verify collision)
101
+ - **Hosted deploy:** target + key storage (or "not yet")
102
+ - **Enterprise shape:** hosted-multitenant-first vs on-prem-first (sequences the enterprise stories)
103
+ - **SWE-bench:** spend to finish 119? (yes/no)
104
+ - **Outreach:** post the MCP-registry/Glama/awesome-list submissions? (yes/no)
105
+
106
+ Everything else, I continue autonomously.
@@ -2,7 +2,7 @@
2
2
 
3
3
  The flagship product of [Autonomi](https://www.autonomi.dev/). Loki Mode is a spec-driven autonomous builder with a built-in trust layer that takes any spec to a deployed product and verifies completion with evidence (quality gates plus a completion council), not just a "done" claim. Complete installation instructions for all platforms and use cases.
4
4
 
5
- **Version:** v7.104.4
5
+ **Version:** v7.105.0
6
6
 
7
7
  ---
8
8
 
@@ -0,0 +1,61 @@
1
+ # Loki Build Speed Diagnosis (2026-07-01)
2
+
3
+ Measured from real build telemetry (anonima `.loki/events.jsonl`, stage_complete +
4
+ iteration timeline), NOT from reading run.sh. Method per advisor: measure before optimizing.
5
+
6
+ ## Data hygiene
7
+ anonima's events.jsonl contained **9 interleaved builds/sessions** (9 session_starts) plus
8
+ session-long manual poking. Naive sums ("5 min/iter x 14") are contaminated. Split on
9
+ session_start and analyzed the real ones.
10
+
11
+ ## The pathological build (session 3): the smoking gun
12
+ A simple topic-based chat app. 14 ACT iterations, ~97 min of real work (idle gaps excluded).
13
+
14
+ | iteration | real work |
15
+ |---|---|
16
+ | 1 | 2.1 min |
17
+ | 6 | 9.9 min |
18
+ | 10 | 15.3 min |
19
+ | 12 | **29.6 min** |
20
+ | (others) | 3-6 min each |
21
+
22
+ **13 completion claims across 14 iterations.** The FIRST claim already stated:
23
+ "All PRD requirements implemented and tests passing. Evidence: npm test => 19/19 pass...
24
+ PRD checklist (14 items) fully implemented." Yet the loop ran 13 more iterations.
25
+
26
+ ## Root cause: NON-CONVERGENCE, not per-stage slowness
27
+ - **Code review / 3-reviewer council is NOT the bottleneck** (0.1-2.8 min per iteration,
28
+ mostly <1 min). Cutting the council would harm accuracy for ~no speed gain. Ruled out.
29
+ - **The bottleneck is iteration COUNT + a few pathological iterations.** The app was
30
+ essentially done early (real passing tests, checklist complete) but the RARV loop's
31
+ "there is NEVER a finished state -- always find the next improvement" behavior kept it
32
+ grinding polish iterations. For a user who wants a fullstack app fast, this is THE
33
+ frustration: loki keeps going after done.
34
+ - iter 10 (15 min) and iter 12 (30 min) did disproportionate work -- candidate over-scoping
35
+ or re-analysis; needs per-iteration content inspection.
36
+
37
+ ## Buckets (advisor framework)
38
+ 1. **WASTE (cut freely):** polish iterations after genuine completion; the PRD-reuse
39
+ spurious-update re-reconciliation (already diagnosed, design fix pending). These add
40
+ wall-clock with no trust value.
41
+ 2. **VERIFICATION COST (keep -- this is the product):** council, gates, completion vote.
42
+ Fast already. The moat. Do NOT cut for speed.
43
+ 3. **STALE SCAFFOLDING (test per-stage):** machinery built for weaker models may be dead
44
+ weight on Opus 4.8 / Sonnet 5 (cf. Anthropic's "context resets became dead weight on
45
+ Opus 4.5"). Hypothesis -- must be tested against the benchmark, not assumed.
46
+
47
+ ## The fix direction (speed AND accuracy, never speed-by-cutting-verification)
48
+ **Convergence:** stop as soon as the completion council genuinely agrees it is done
49
+ (honest verified completion), instead of running "next improvement" iterations. This is a
50
+ SPEED win that INCREASES trust alignment (stop when verified-done = the moat working), not a
51
+ verification cut. The user's mandate is "accuracy AND speed"; convergence delivers both.
52
+
53
+ ## HARD PREREQUISITE (before any council/RARV-C change): the benchmark
54
+ Both the speed directive and the "update council/RARV-C to latest research, show before/after
55
+ accuracy" directive require a **clean reproducible benchmark on a fixed spec** emitting
56
+ wall-clock + iteration count + completion verdict + pass/fail vs known acceptance criteria.
57
+ Without it, changing the verification core is unmeasurable churn on the moat (architecture-
58
+ level fake-green). Build the benchmark FIRST, capture before-numbers, fix the clearest waste
59
+ (convergence), re-run, show real before->after. Then gate any council/RARV-C change on that
60
+ benchmark, one named change at a time, kept only if accuracy goes up and wall-clock does not
61
+ regress.
@@ -0,0 +1,122 @@
1
+ # Loki + Autonomi: Top-100 Backlog (Master Plan)
2
+
3
+ Generated 2026-07-01 by the top100-backlog-enumeration ultracode workflow (7 agents, 116 raw items -> 100 ranked). North star: easiest to integrate with anyone; ranked by leverage toward zero-friction adoption, modularization, enterprise-grade, and scale.
4
+
5
+ ## Themes
6
+ - Trust and correctness bugs (data-loss, cross-tenant, fake-green safety)
7
+ - Autonomi Verify productization (stub -> shipped: gate/council/cost/sign wiring)
8
+ - Zero-friction adoption (reduce user setup and error surface)
9
+ - Modularization (extract libraries, break monoliths, fix import boundaries)
10
+ - Enterprise-scale and multi-tenant (RBAC/SSO/persistence/sandbox, mostly founder-gated)
11
+ - autonomi-saas evidence and proof pipeline (receipt phases, correlation, worker cutover)
12
+ - CI, test, and distribution hardening (coverage, unreachable tests, publish gates)
13
+ - Runtime-agnostic and Bun migration (bash sunset, write-path port)
14
+
15
+ ## Legend
16
+ - **gate**: autonomous (I execute) | founder-gated (prep to one-approval-away) | community
17
+ - **effort**: S/M/L/XL | **value**: critical/high/medium/low
18
+
19
+ ## Full ranked list
20
+
21
+ | # | value | eff | gate | area | item |
22
+ |---|---|---|---|---|---|
23
+ | 1 | critical | S | autonomous | engine | run.sh self-delete EXIT trap nukes canonical source on LOKI_RUNNING_FROM_TEMP |
24
+ | 2 | critical | M | autonomous | saas | autonomi-saas proof correlation: cross-tenant leak + stuck-Building fix (per-build run_id) |
25
+ | 3 | critical | XL | founder-gated | verify | Verify: verifyCompletion() stub throws not-implemented (wire gate->integrity->council->cost->sign pipeline) |
26
+ | 4 | critical | M | founder-gated | verify | Verify: npm @autonomi/verify unpublishable (private:true, no exports/types/build, .ts bin, undeclared zod) |
27
+ | 5 | critical | S | founder-gated | saas | Verify: BUSL-1.1 vs open licensing undecided (blocks hosted/commercial launch) |
28
+ | 6 | critical | M | founder-gated | saas | Verify: hosted /verify REST API not deployed (Node entry + Dockerfile + serve script + persistent signing key) |
29
+ | 7 | critical | M | autonomous | modularization | Verify: extract verification-core library (port council_evidence_gate to TS) |
30
+ | 8 | critical | S | autonomous | verify | Verify: 5 deterministic gates parity with completion-council.sh (staged, verify + ship) |
31
+ | 9 | critical | S | autonomous | saas | autonomi-saas anti-fake-green safety: verdict strictly from engine honesty.headline (verify + guard) |
32
+ | 10 | critical | M | autonomous | saas | autonomi-saas VS-7 worker cutover: BFF enqueues, worker sole executor (verify live e2e) |
33
+ | 11 | high | M | founder-gated | verify | Signed neutral records on SDK/MCP path (verifyCompletion returns UNSIGNED_SENTINEL; only REST signs) |
34
+ | 12 | medium | S | autonomous | verify | Verify: fix embed import boundary (review/* drags LLM council into offline core; refresh regresses) |
35
+ | 13 | high | S | founder-gated | verify | Verify: resolve naming collision (loki-vaas vs loki-verify bin vs loki verify subcommand) |
36
+ | 14 | critical | L | founder-gated | engine | Sandbox backend implementation (createSandbox throws; choose gVisor/Firecracker/Kata) |
37
+ | 15 | critical | XL | founder-gated | enterprise | Enterprise RBAC / SSO / API-key auth / metering (hosted + on-prem control plane) |
38
+ | 16 | high | L | founder-gated | enterprise | On-prem persistence backend interface + first prod store (Postgres/SQLite) |
39
+ | 17 | high | S | autonomous | engine | base_unreachable signal in ObservedDiff (suppress empty_diff false-block once sandbox lands) |
40
+ | 18 | high | M | founder-gated | engine | Real Anthropic LLM provider wiring for production verify (council against live model) |
41
+ | 19 | high | M | founder-gated | verify | Verify: LLM council wired into default verdict path (module exists, verifyCompletion stub does not invoke) |
42
+ | 20 | high | S | autonomous | engine | Signed verification records + ed25519 audit trail (staged, verify + ship) |
43
+ | 21 | high | M | autonomous | verify | Golden-output conformance corpus for byte-identical CLI-invariance across implementations |
44
+ | 22 | high | M | autonomous | engine | Code review gate: run 3 parallel reviewers (currently single-reviewer path) |
45
+ | 23 | high | S | founder-gated | saas | HTTP /verify endpoint production wiring (handler complete, README lists as planned) |
46
+ | 24 | high | S | autonomous | saas | MCP verify tools (verify_completion, run_evidence_gate) wiring + distribution (staged) |
47
+ | 25 | high | M | community | test | Memory failure-loop (FAILURE_MEMORY) persistence bug (strict-xfail; recency read incomplete) |
48
+ | 26 | high | XL | autonomous | engine | Bun runtime Phase 2: migrate write-path commands (feat/bun-migration) |
49
+ | 27 | high | M | autonomous | cli | Bash runtime sunset (loki run-shell removal, Phase 6, after clean soak) |
50
+ | 28 | high | M | community | saas | autonomi-saas Evidence Receipt Phase 2: screenshot capture + BFF stream route (in progress) |
51
+ | 29 | high | M | community | saas | autonomi-saas Evidence Receipt Phase 3: test results from engine workspace |
52
+ | 30 | high | L | autonomous | saas | autonomi-saas Cost tab + budget-breaker UX (web + enforcement audit) |
53
+ | 31 | high | S | community | saas | autonomi-saas BFF overlays stored saas_evidence onto engine proof (verify) |
54
+ | 32 | medium | M | founder-gated | saas | E4 no-server run output capture for CLI/library builds (bounded inline + artifact) |
55
+ | 33 | high | M | autonomous | dashboard | Prompt Optimizer: wire real LLM-as-judge (currently heuristic-only) |
56
+ | 34 | high | M | community | engine | Cost-honesty check implementation (normalizeModel + quote==dispatched) |
57
+ | 35 | high | S | community | saas | POST /cost-honesty API handler (blocked on cost core) |
58
+ | 36 | critical | L | founder-gated | saas | Hosted metered runner with per-verification container sandbox |
59
+ | 37 | high | M | community | ci | CI parity-matrix flake root-cause (state cooldown after test-cli-commands.sh) |
60
+ | 38 | high | M | community | ci | Post-release smoke sign-off gate before npm/Docker go public |
61
+ | 39 | high | M | community | test | Document the 3 pre-existing failing shell tests (no public known-failures list) |
62
+ | 40 | medium | S | autonomous | dashboard | PR #178: dashboard memory summary reports 0 on JSON-backed projects |
63
+ | 41 | high | M | community | test | ARM64 runtime verification (emulation-only in CI; needs real Apple Silicon run) |
64
+ | 42 | high | XL | founder-gated | saas | Multi-tenant sandbox backend (gVisor/Firecracker/Kata) selection spike |
65
+ | 43 | critical | L | community | test | Real provider end-to-end tests (cost + secrets + duration make CI-unreachable) |
66
+ | 44 | high | XL | founder-gated | enterprise | Enterprise deploy: object-store sync + queue fleet modes + #691 --config loader |
67
+ | 45 | high | M | founder-gated | enterprise | Compliance scanner (loki-verify) converged as module in trust-layer SKU |
68
+ | 46 | high | L | founder-gated | saas | GitHub App verification integration (webhook wrapper, greenfield) |
69
+ | 47 | high | L | community | test | Long-duration stress tests (leak/queue-churn) unreachable in CI |
70
+ | 48 | medium | L | community | saas | autonomi-saas Evidence Receipt Phase 4: clip + full test-log streaming + build_artifacts table |
71
+ | 49 | high | XL | community | test | SWE-bench Pro 119 resume (paused 35/119) |
72
+ | 50 | medium | M | autonomous | engine | Multi-reviewer completion council option in hosted verify (council:true dropped today) |
73
+ | 51 | medium | M | autonomous | engine | Integrity detectors (mock-only, tautological, test-weakening) wired into evidence block |
74
+ | 52 | medium | S | community | test | Bun test coverage threshold enforcement on branches (70% baseline exists) |
75
+ | 53 | medium | L | autonomous | dashboard | Dashboard mid-run steering / builder control (mid-flight model switch exists, broader control deferred) |
76
+ | 54 | medium | S | community | test | Dashboard static-asset freshness gate in local-ci (like loki-ts/dist) |
77
+ | 55 | medium | L | community | test | Windows/WSL runtime verification (no Windows CI runner) |
78
+ | 56 | low | M | autonomous | cli | KPI reporting (loki report kpis) on bash legacy route (Bun-only today) |
79
+ | 57 | medium | M | autonomous | cli | Voice mode (Issue #85) |
80
+ | 58 | medium | M | community | test | Mutation testing (stryker) workflow re-enable + fix stale 5-provider assertions |
81
+ | 59 | medium | XL | founder-gated | engine | Managed Agents multiagent path (LOKI_EXPERIMENTAL_MANAGED_*, research preview) |
82
+ | 60 | low | M | autonomous | engine | Sentrux iteration-loop auto-gating (manual-only today) |
83
+ | 61 | medium | S | founder-gated | adoption | npm registry publish prep for @autonomi/verify SDK (distribution wiring) |
84
+ | 62 | low | M | founder-gated | adoption | Disclosed default-on PostHog telemetry (Phase L, blocked + deferred) |
85
+ | 63 | medium | S | community | saas | MCP check_cost_honesty tool registration (deferred until cost core lands) |
86
+ | 64 | medium | L | founder-gated | dashboard | Hosted dashboard with shareable verification badges |
87
+ | 65 | medium | M | founder-gated | adoption | MCP registry submission + Glama claim + awesome-list PRs |
88
+ | 66 | medium | M | autonomous | saas | autonomi-saas UI/UX pass: build-watching pane + finished cues from trust verdict |
89
+ | 67 | medium | XL | autonomous | dashboard | Server.py route modularization (~10K-line monolith, no domain separation) |
90
+ | 68 | medium | L | autonomous | dashboard | Large dashboard-ui components modularization (5 files 1K-1.9K lines) |
91
+ | 69 | medium | L | founder-gated | engine | Wave-13 deferred trust fixes + PRD-reuse spurious-update design fix |
92
+ | 70 | medium | M | community | dashboard | Dashboard dark-toggle iframe theme not following SPA (harness/product parity) |
93
+ | 71 | medium | M | community | ci | Sonnet-5 calibration follow-ups: test-verify --hosted + gate missing-tool handling |
94
+ | 72 | medium | S | community | ci | Integrity audit (SBOM/provenance) blocking on publish (currently informational) |
95
+ | 73 | medium | S | community | distribution | Python SDK version pre-check before publish (mismatch risk if publish fails post-tag) |
96
+ | 74 | medium | S | community | ci | Docker buildx early-detection for uncommitted loki-ts/dist (silent COPY failure risk) |
97
+ | 75 | medium | S | community | ci | loki-enterprise workflow enabled in main CI (dispatch-only today) |
98
+ | 76 | medium | S | community | distribution | Brew tap real-install verification on clean macOS host post-release |
99
+ | 77 | low | L | founder-gated | perf | Horizontal scaling / distributed rate-limit + persistence (in-memory today) |
100
+ | 78 | low | S | community | saas | Optional build_artifacts table for enumeration/lifecycle/reaping (deferred) |
101
+ | 79 | medium | S | community | saas | Headless browser (playwright-core) dep + Docker image size cap for screenshot capture |
102
+ | 80 | low | M | community | engine | Wave-8/9 deferred tail: spawn_timeout removal, heredoc guards, MED/LOW findings |
103
+ | 81 | medium | L | community | adoption | v7.19.1 traction: uncertainty-gated escalation + shareable proof + integrate 8 tutorials |
104
+ | 82 | medium | M | autonomous | verify | Benchmarks HumanEval/SWE-bench: publish results (runners + datasets exist) |
105
+ | 83 | low | S | autonomous | cli | loki run removal (deprecated alias for loki start, next major) |
106
+ | 84 | low | S | autonomous | cli | Fable model tier wiring (collapses to Opus until API-available) |
107
+ | 85 | low | S | community | distribution | Homepage/blog release update automation (Slack-notify-only today) |
108
+ | 86 | low | S | community | distribution | Wiki-sync scheduled trigger (manual/dispatch only, skips without API key) |
109
+ | 87 | low | L | autonomous | dashboard | Dashboard React app: implement four stub views (experimental app) |
110
+ | 88 | low | S | community | test | Embeddings edge-case tests run without numpy (skip today) |
111
+ | 89 | low | S | community | test | Hybrid-search test runs without chromadb skip |
112
+ | 90 | low | S | community | test | PID-recycling test platform-portability (ps lstart skip for pid 1) |
113
+ | 91 | low | S | community | test | Cloud integration tests (aider-cloud) run in a credentialed gated job |
114
+ | 92 | low | S | community | test | Sentrux real-binary nightly promoted from best-effort to tracked |
115
+ | 93 | low | S | community | test | Model-catalog probe: surface staleness in tests (issue-create skipped without admin scope) |
116
+ | 94 | low | S | community | test | Phase 6 readiness check re-enable (cron disabled; issues disabled on repo) |
117
+ | 95 | low | S | community | distribution | TS/Bun SDK (@loki-mode/sdk) discoverability from main package/docs |
118
+ | 96 | low | S | community | distribution | Docker Hub description update resilience (PAT scope failures non-blocking) |
119
+ | 97 | low | S | community | distribution | VS Code extension: keep source marked deprecated, not silently removed |
120
+ | 98 | low | S | autonomous | dashboard | Web-app docs: fill video placeholder (coming-soon text) |
121
+ | 99 | low | M | autonomous | cli | C# provider support (roslyn analyzers + dotnet build, deferred) |
122
+ | 100 | low | XL | founder-gated | engine | BMAD-METHOD v6 integration (net-new, needs founder planning) |
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- var X8=Object.defineProperty;var K8=($)=>$;function q8($,Q){this[$]=K8.bind(null,Q)}var b=($,Q)=>{for(var Z in Q)X8($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:q8.bind(Q,Z)})};var L=($,Q)=>()=>($&&(Q=$($=0)),Q);var J$=import.meta.require;var v1={};b(v1,{lokiDir:()=>P,homeLokiDir:()=>T$,findRepoRootForVersion:()=>e$,REPO_ROOT:()=>h});import{resolve as r,dirname as i$}from"path";import{fileURLToPath as J8}from"url";import{existsSync as x$}from"fs";import{homedir as V8}from"os";function W8(){let $=y1;for(let Q=0;Q<6;Q++){if(x$(r($,"VERSION"))&&x$(r($,"autonomy/run.sh")))return $;let Z=i$($);if(Z===$)break;$=Z}return r(y1,"..","..","..")}function e$($){let Q=$;for(let Z=0;Z<6;Z++){if(x$(r(Q,"VERSION"))&&x$(r(Q,"autonomy/run.sh")))return Q;let z=i$(Q);if(z===Q)break;Q=z}return r($,"..","..","..")}function P(){return process.env.LOKI_DIR??r(process.cwd(),".loki")}function T$(){return r(V8(),".loki")}var y1,h;var C=L(()=>{y1=i$(J8(import.meta.url));h=W8()});import{readFileSync as U8}from"fs";import{resolve as H8,dirname as G8}from"path";import{fileURLToPath as B8}from"url";function N$(){if(Q$!==null)return Q$;let $="7.104.4";if(typeof $==="string"&&$.length>0)return Q$=$,Q$;try{let Q=G8(B8(import.meta.url)),Z=e$(Q);Q$=U8(H8(Z,"VERSION"),"utf-8").trim()}catch{Q$="unknown"}return Q$}var Q$=null;var $1=L(()=>{C()});var c1={};b(c1,{runOrThrow:()=>x8,run:()=>F,readStreamCapped:()=>u1,commandVersion:()=>S8,commandExists:()=>f,ShellError:()=>Q1,MAX_STDOUT_BYTES:()=>f1});async function u1($,Q=f1){let Z=$.getReader(),z=new TextDecoder,X="",q=0;try{while(q<Q){let{done:K,value:W}=await Z.read();if(K)break;if(!W)continue;if(q+=W.byteLength,q>Q){let V=W.byteLength-(q-Q);X+=z.decode(W.subarray(0,V),{stream:!0});break}X+=z.decode(W,{stream:!0})}X+=z.decode()}finally{try{await Z.cancel()}catch{}Z.releaseLock()}return X}async function F($,Q={}){let Z=Bun.spawn({cmd:[...$],stdout:"pipe",stderr:"pipe",env:Q.env?{...process.env,...Q.env}:process.env,cwd:Q.cwd}),z,X;if(Q.timeoutMs&&Q.timeoutMs>0)z=setTimeout(()=>{try{Z.kill("SIGTERM")}catch{}X=setTimeout(()=>{try{Z.kill("SIGKILL")}catch{}},2000)},Q.timeoutMs);try{let[q,K,W]=await Promise.all([u1(Z.stdout),new Response(Z.stderr).text(),Z.exited]);return{stdout:q,stderr:K,exitCode:W}}finally{if(z)clearTimeout(z);if(X)clearTimeout(X)}}async function x8($,Q={}){let Z=await F($,Q);if(Z.exitCode!==0)throw new Q1(`command failed (${Z.exitCode}): ${$.join(" ")}`,Z.exitCode,Z.stdout,Z.stderr);return Z}async function f($){let Q=N8($),Z=await F(["sh","-c",`command -v ${Q}`],{timeoutMs:5000});if(Z.exitCode===0)return Z.stdout.trim()||null;return null}function N8($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function S8($,Q="--version"){if(!await f($))return null;let z=await F([$,Q],{timeoutMs:5000});if(z.exitCode!==0)return null;return((z.stdout||z.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var f1=16777216,Q1;var d=L(()=>{Q1=class Q1 extends Error{message;exitCode;stdout;stderr;constructor($,Q,Z,z){super($);this.message=$;this.exitCode=Q;this.stdout=Z;this.stderr=z;this.name="ShellError"}}});function t($){return D8?"":$}var D8,T,S,_,nZ,I,k,y,J;var c=L(()=>{D8=(process.env.NO_COLOR??"").length>0;T=t("\x1B[0;31m"),S=t("\x1B[0;32m"),_=t("\x1B[1;33m"),nZ=t("\x1B[0;34m"),I=t("\x1B[0;36m"),k=t("\x1B[1m"),y=t("\x1B[2m"),J=t("\x1B[0m")});import{existsSync as c8}from"fs";async function Z$(){if(A$!==void 0)return A$;let $="/opt/homebrew/bin/python3.12";if(c8($))return A$=$,$;let Q=await f("python3.12");if(Q)return A$=Q,Q;let Z=await f("python3");return A$=Z,Z}async function z$($,Q={}){let Z=await Z$();if(!Z)return{stdout:"",stderr:"python3 not found",exitCode:127};return F([Z,"-c",$],Q)}var A$;var V$=L(()=>{d()});var J0={};b(J0,{runStatus:()=>U3});import{existsSync as v,readFileSync as U$,readdirSync as e1,statSync as $0}from"fs";import{resolve as D,basename as Q3}from"path";import{homedir as Z3}from"os";function Q0($){let Q=Math.trunc($);if(Q>=1e6)return`${(Math.trunc(Q/1e6*10)/10).toFixed(1)}M`;if(Q>=1000)return`${(Math.trunc(Q/1000*10)/10).toFixed(1)}K`;return String(Q)}function Z0($,Q,Z){if(Q===0)return null;let z=Math.trunc($*100/Q),X=Math.trunc($*D$/Q);if(X>D$)X=D$;let q=D$-X,K=S;if(z>=80)K=T;else if(z>=50)K=_;let W="=".repeat(Math.max(0,X))+" ".repeat(Math.max(0,q)),V=Q0($),U=Q0(Q);return` ${k}${Z}${J} ${K}[${W}]${J} ${z}% (${V} / ${U})`}async function X3(){if(await f("jq"))return!0;return process.stdout.write(`${T}Error: jq is required but not installed.${J}
2
+ var X8=Object.defineProperty;var K8=($)=>$;function q8($,Q){this[$]=K8.bind(null,Q)}var b=($,Q)=>{for(var Z in Q)X8($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:q8.bind(Q,Z)})};var L=($,Q)=>()=>($&&(Q=$($=0)),Q);var J$=import.meta.require;var v1={};b(v1,{lokiDir:()=>P,homeLokiDir:()=>T$,findRepoRootForVersion:()=>e$,REPO_ROOT:()=>h});import{resolve as r,dirname as i$}from"path";import{fileURLToPath as J8}from"url";import{existsSync as x$}from"fs";import{homedir as V8}from"os";function W8(){let $=y1;for(let Q=0;Q<6;Q++){if(x$(r($,"VERSION"))&&x$(r($,"autonomy/run.sh")))return $;let Z=i$($);if(Z===$)break;$=Z}return r(y1,"..","..","..")}function e$($){let Q=$;for(let Z=0;Z<6;Z++){if(x$(r(Q,"VERSION"))&&x$(r(Q,"autonomy/run.sh")))return Q;let z=i$(Q);if(z===Q)break;Q=z}return r($,"..","..","..")}function P(){return process.env.LOKI_DIR??r(process.cwd(),".loki")}function T$(){return r(V8(),".loki")}var y1,h;var C=L(()=>{y1=i$(J8(import.meta.url));h=W8()});import{readFileSync as U8}from"fs";import{resolve as H8,dirname as G8}from"path";import{fileURLToPath as B8}from"url";function N$(){if(Q$!==null)return Q$;let $="7.105.0";if(typeof $==="string"&&$.length>0)return Q$=$,Q$;try{let Q=G8(B8(import.meta.url)),Z=e$(Q);Q$=U8(H8(Z,"VERSION"),"utf-8").trim()}catch{Q$="unknown"}return Q$}var Q$=null;var $1=L(()=>{C()});var c1={};b(c1,{runOrThrow:()=>x8,run:()=>F,readStreamCapped:()=>u1,commandVersion:()=>S8,commandExists:()=>f,ShellError:()=>Q1,MAX_STDOUT_BYTES:()=>f1});async function u1($,Q=f1){let Z=$.getReader(),z=new TextDecoder,X="",q=0;try{while(q<Q){let{done:K,value:W}=await Z.read();if(K)break;if(!W)continue;if(q+=W.byteLength,q>Q){let V=W.byteLength-(q-Q);X+=z.decode(W.subarray(0,V),{stream:!0});break}X+=z.decode(W,{stream:!0})}X+=z.decode()}finally{try{await Z.cancel()}catch{}Z.releaseLock()}return X}async function F($,Q={}){let Z=Bun.spawn({cmd:[...$],stdout:"pipe",stderr:"pipe",env:Q.env?{...process.env,...Q.env}:process.env,cwd:Q.cwd}),z,X;if(Q.timeoutMs&&Q.timeoutMs>0)z=setTimeout(()=>{try{Z.kill("SIGTERM")}catch{}X=setTimeout(()=>{try{Z.kill("SIGKILL")}catch{}},2000)},Q.timeoutMs);try{let[q,K,W]=await Promise.all([u1(Z.stdout),new Response(Z.stderr).text(),Z.exited]);return{stdout:q,stderr:K,exitCode:W}}finally{if(z)clearTimeout(z);if(X)clearTimeout(X)}}async function x8($,Q={}){let Z=await F($,Q);if(Z.exitCode!==0)throw new Q1(`command failed (${Z.exitCode}): ${$.join(" ")}`,Z.exitCode,Z.stdout,Z.stderr);return Z}async function f($){let Q=N8($),Z=await F(["sh","-c",`command -v ${Q}`],{timeoutMs:5000});if(Z.exitCode===0)return Z.stdout.trim()||null;return null}function N8($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function S8($,Q="--version"){if(!await f($))return null;let z=await F([$,Q],{timeoutMs:5000});if(z.exitCode!==0)return null;return((z.stdout||z.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var f1=16777216,Q1;var d=L(()=>{Q1=class Q1 extends Error{message;exitCode;stdout;stderr;constructor($,Q,Z,z){super($);this.message=$;this.exitCode=Q;this.stdout=Z;this.stderr=z;this.name="ShellError"}}});function t($){return D8?"":$}var D8,T,S,_,nZ,I,k,y,J;var c=L(()=>{D8=(process.env.NO_COLOR??"").length>0;T=t("\x1B[0;31m"),S=t("\x1B[0;32m"),_=t("\x1B[1;33m"),nZ=t("\x1B[0;34m"),I=t("\x1B[0;36m"),k=t("\x1B[1m"),y=t("\x1B[2m"),J=t("\x1B[0m")});import{existsSync as c8}from"fs";async function Z$(){if(A$!==void 0)return A$;let $="/opt/homebrew/bin/python3.12";if(c8($))return A$=$,$;let Q=await f("python3.12");if(Q)return A$=Q,Q;let Z=await f("python3");return A$=Z,Z}async function z$($,Q={}){let Z=await Z$();if(!Z)return{stdout:"",stderr:"python3 not found",exitCode:127};return F([Z,"-c",$],Q)}var A$;var V$=L(()=>{d()});var J0={};b(J0,{runStatus:()=>U3});import{existsSync as v,readFileSync as U$,readdirSync as e1,statSync as $0}from"fs";import{resolve as D,basename as Q3}from"path";import{homedir as Z3}from"os";function Q0($){let Q=Math.trunc($);if(Q>=1e6)return`${(Math.trunc(Q/1e6*10)/10).toFixed(1)}M`;if(Q>=1000)return`${(Math.trunc(Q/1000*10)/10).toFixed(1)}K`;return String(Q)}function Z0($,Q,Z){if(Q===0)return null;let z=Math.trunc($*100/Q),X=Math.trunc($*D$/Q);if(X>D$)X=D$;let q=D$-X,K=S;if(z>=80)K=T;else if(z>=50)K=_;let W="=".repeat(Math.max(0,X))+" ".repeat(Math.max(0,q)),V=Q0($),U=Q0(Q);return` ${k}${Z}${J} ${K}[${W}]${J} ${z}% (${V} / ${U})`}async function X3(){if(await f("jq"))return!0;return process.stdout.write(`${T}Error: jq is required but not installed.${J}
3
3
  `),process.stdout.write(`Install with:
4
4
  `),process.stdout.write(` brew install jq (macOS)
5
5
  `),process.stdout.write(` apt install jq (Debian/Ubuntu)
@@ -805,4 +805,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
805
805
  `),2}default:return process.stderr.write(`Unknown command: ${Q}
806
806
  `),process.stderr.write(z8),2}}i1();process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var EZ=await RZ(Bun.argv.slice(2));process.exit(EZ);
807
807
 
808
- //# debugId=61E42E2EF5F94BA264756E2164756E21
808
+ //# debugId=80BB634E7BCAD6EC64756E2164756E21
package/mcp/__init__.py CHANGED
@@ -57,4 +57,4 @@ try:
57
57
  except ImportError:
58
58
  __all__ = ['mcp']
59
59
 
60
- __version__ = '7.104.4'
60
+ __version__ = '7.105.0'
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "loki-mode",
3
3
  "mcpName": "io.github.asklokesh/loki-mode",
4
- "version": "7.104.4",
4
+ "version": "7.105.0",
5
5
  "description": "Loki Mode by Autonomi. Autonomous spec-to-product system: takes a PRD, GitHub issue, OpenAPI/JSON/YAML, or one-line brief to a deployed app via the RARV-C closure loop with 8 quality gates. Provider-agnostic (Claude Code, OpenAI Codex, Cline, Aider).",
6
6
  "keywords": [
7
7
  "agent",
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
3
3
  "name": "loki-mode",
4
4
  "displayName": "Loki Mode",
5
- "version": "7.104.4",
5
+ "version": "7.105.0",
6
6
  "description": "Autonomous spec-to-product build system with a built-in trust layer (RARV-C closure loop, 8 quality gates, completion council). Ships Loki's spec-hardening, drift-detection, and deterministic PR verification commands plus the Loki MCP server.",
7
7
  "author": {
8
8
  "name": "Autonomi",