loki-mode 9.59.0 → 9.63.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 v9.59.0
6
+ # Loki Mode v9.63.0
7
7
 
8
8
  **You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
9
9
 
@@ -470,4 +470,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
470
470
 
471
471
  ---
472
472
 
473
- **v9.59.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~410 lines core**
473
+ **v9.63.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~410 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 9.59.0
1
+ 9.63.0
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env bash
2
+ # Loki Mode -- the single guarded browser opener (S-103).
3
+ #
4
+ # Every place Loki opens a browser MUST go through loki_open_url. Tests and
5
+ # agent sessions must never pop a browser tab on a developer's machine (a
6
+ # test run once opened dozens of "Loki Mode" tabs at an offline dashboard).
7
+ # tests/test-browser-open-guard.sh fails if a raw opener call appears anywhere
8
+ # else.
9
+ #
10
+ # Public API:
11
+ # loki_browser_allowed -> 0 when a browser may be opened, 1 otherwise
12
+ # loki_open_url <target> -> 0 when an opener ran, 1 when suppressed or no
13
+ # opener exists (caller prints the URL instead)
14
+ #
15
+ # Suppressed when any of these hold:
16
+ # LOKI_NO_BROWSER=1 or LOKI_NO_AUTO_OPEN=1 (explicit opt-out)
17
+ # CI is set
18
+ # LOKI_TEST, BATS_VERSION, BATS_TEST_FILENAME or PYTEST_CURRENT_TEST is set
19
+ # stdout is not a TTY
20
+ #
21
+ # Call loki_open_url WITHOUT redirecting its stdout: the TTY check reads fd 1.
22
+ # The helper silences the opener itself.
23
+
24
+ if [ "${__LOKI_BROWSER_OPEN_SH_LOADED:-0}" = "1" ]; then
25
+ return 0 2>/dev/null || true
26
+ fi
27
+ __LOKI_BROWSER_OPEN_SH_LOADED=1
28
+
29
+ loki_browser_allowed() {
30
+ [ "${LOKI_NO_BROWSER:-0}" = "1" ] && return 1
31
+ [ "${LOKI_NO_AUTO_OPEN:-0}" = "1" ] && return 1
32
+ [ -n "${CI:-}" ] && return 1
33
+ [ -n "${LOKI_TEST:-}${BATS_VERSION:-}${BATS_TEST_FILENAME:-}${PYTEST_CURRENT_TEST:-}" ] && return 1
34
+ [ -t 1 ] || return 1
35
+ return 0
36
+ }
37
+
38
+ loki_open_url() {
39
+ local target="$1"
40
+ loki_browser_allowed || return 1
41
+ if command -v open >/dev/null 2>&1; then
42
+ open "$target" >/dev/null 2>&1
43
+ elif command -v xdg-open >/dev/null 2>&1; then
44
+ xdg-open "$target" >/dev/null 2>&1
45
+ elif command -v cmd.exe >/dev/null 2>&1; then
46
+ # Windows (Git Bash/WSL): `start` is a cmd builtin; "" is its title arg.
47
+ cmd.exe /c start "" "$target" >/dev/null 2>&1
48
+ else
49
+ return 1
50
+ fi
51
+ }
@@ -29,17 +29,29 @@ _LINEAGE_DISCOVERY_WINDOW_SECONDS = 0.20
29
29
  _RECONCILE_SCHEDULING_MARGIN_SECONDS = 0.30
30
30
  _LINEAGE_ENV_NAME = "LOKI_DEADLINE_LINEAGE"
31
31
  _CONTROL_ENV_NAME = "LOKI_DEADLINE_CONTROL_FILE"
32
+ # How long spawn_tracked waits for the bootstrap to report that it is running.
33
+ # Exceeding it fails the launch closed; it never skips the lineage check.
34
+ _LAUNCH_READY_TIMEOUT_SECONDS = 30.0
35
+
36
+ # The ready byte is written by interpreted code, so execve has fully returned
37
+ # to userspace and the kernel has published the new environment block. Popen
38
+ # returning is NOT that point: its exec-error pipe closes at close-on-exec time,
39
+ # before the ELF loader sets mm->env_end, and /proc/<pid>/environ reads as empty
40
+ # for a live child in that window.
32
41
  _EXEC_BOOTSTRAP = """
33
42
  import os
34
43
  import sys
35
44
 
36
- gate = int(sys.argv[1])
45
+ ready = int(sys.argv[1])
46
+ os.write(ready, b"1")
47
+ os.close(ready)
48
+ gate = int(sys.argv[2])
37
49
  released = os.read(gate, 1)
38
50
  os.close(gate)
39
51
  if released != b"1":
40
52
  raise SystemExit(125)
41
53
  try:
42
- os.execvpe(sys.argv[2], sys.argv[2:], os.environ)
54
+ os.execvpe(sys.argv[3], sys.argv[3:], os.environ)
43
55
  except FileNotFoundError:
44
56
  raise SystemExit(127)
45
57
  except PermissionError:
@@ -585,6 +597,7 @@ def spawn_tracked(
585
597
  process: subprocess.Popen[bytes] | None = None
586
598
  gate_read = gate_write = -1
587
599
  lineage_read = lineage_write = -1
600
+ ready_read = ready_write = -1
588
601
  try:
589
602
  subreaper = _configure_child_subreaper()
590
603
  token = secrets.token_hex(16)
@@ -594,6 +607,7 @@ def spawn_tracked(
594
607
  inherited = tuple(popen_kwargs.pop("pass_fds", ()))
595
608
  gate_read, gate_write = os.pipe()
596
609
  lineage_read, lineage_write = os.pipe()
610
+ ready_read, ready_write = os.pipe()
597
611
  handles = (
598
612
  _darwin_pipe_handles(os.getpid(), lineage_read)
599
613
  if sys.platform == "darwin"
@@ -602,14 +616,34 @@ def spawn_tracked(
602
616
  if sys.platform == "darwin" and not handles:
603
617
  raise RuntimeError("attempt lineage pipe identity is unavailable")
604
618
  process = subprocess.Popen(
605
- [sys.executable, "-c", _EXEC_BOOTSTRAP, str(gate_read), *command],
619
+ [
620
+ sys.executable,
621
+ "-c",
622
+ _EXEC_BOOTSTRAP,
623
+ str(ready_write),
624
+ str(gate_read),
625
+ *command,
626
+ ],
606
627
  start_new_session=True,
607
628
  env=child_env,
608
- pass_fds=(*inherited, gate_read, lineage_write),
629
+ pass_fds=(*inherited, ready_write, gate_read, lineage_write),
609
630
  **popen_kwargs,
610
631
  )
611
632
  os.close(gate_read)
612
633
  gate_read = -1
634
+ os.close(ready_write)
635
+ ready_write = -1
636
+ # Wait until the bootstrap is running before reading its marker. EOF
637
+ # without a byte means it died first; the tracker's reaped-child path
638
+ # (poll) owns that case, so fall through to it.
639
+ with selectors.DefaultSelector() as ready_wait:
640
+ ready_wait.register(ready_read, selectors.EVENT_READ)
641
+ readable = ready_wait.select(_LAUNCH_READY_TIMEOUT_SECONDS)
642
+ if not readable:
643
+ raise RuntimeError("provider attempt did not report ready")
644
+ os.read(ready_read, 1)
645
+ os.close(ready_read)
646
+ ready_read = -1
613
647
  tracker = _LineageTracker(process, subreaper, token, handles)
614
648
  tracker._lineage_read_fd = lineage_read
615
649
  lineage_read = -1
@@ -620,7 +654,14 @@ def spawn_tracked(
620
654
  lineage_write = -1
621
655
  return process, tracker
622
656
  except BaseException:
623
- for descriptor in (gate_read, gate_write, lineage_read, lineage_write):
657
+ for descriptor in (
658
+ gate_read,
659
+ gate_write,
660
+ lineage_read,
661
+ lineage_write,
662
+ ready_read,
663
+ ready_write,
664
+ ):
624
665
  if descriptor >= 0:
625
666
  try:
626
667
  os.close(descriptor)
@@ -42,7 +42,14 @@ _PROOF_CHECK_SH=1
42
42
  _proof_check_net() {
43
43
  # run.sh withholds GitHub tokens from agent sessions (Rule of Two); this is
44
44
  # one of Loki's own trusted calls, so it takes them back for the command.
45
- if declare -f _loki_with_github_tokens >/dev/null 2>&1; then
45
+ if declare -f _loki_run_neutral >/dev/null 2>&1; then
46
+ # BACKLOG 149 round 5: run from / with GH_REPO read as data first, so
47
+ # gh's own git calls never load the agent's repo config while holding
48
+ # the real credentials.
49
+ local _pc_repo
50
+ _pc_repo="$(_loki_trusted_repo)"
51
+ _loki_with_github_tokens _loki_run_neutral "$_pc_repo" _proof_check_net_timed "$@"
52
+ elif declare -f _loki_with_github_tokens >/dev/null 2>&1; then
46
53
  _loki_with_github_tokens _proof_check_net_timed "$@"
47
54
  else
48
55
  _proof_check_net_timed "$@"
@@ -197,8 +204,16 @@ post_verified_completion_check() {
197
204
 
198
205
  # --- Resolve owner/repo (nameWithOwner). ----------------------------------
199
206
  # Prefer the current repo context (Loki's model is same-repo branch PRs).
207
+ # Under run.sh, gh runs from / (BACKLOG 149 round 5) and `gh repo view`
208
+ # with no argument IGNORES GH_REPO, so it could never resolve the repo
209
+ # there. Use the origin repo run.sh already reads as data and validates.
210
+ # Standalone (no run.sh helpers), gh runs in the cwd repo as before.
200
211
  local repo=""
201
- repo="$(_proof_check_net gh repo view --json nameWithOwner -q .nameWithOwner 2>/dev/null || true)"
212
+ if declare -f _loki_trusted_repo >/dev/null 2>&1; then
213
+ repo="$(_loki_trusted_repo)"
214
+ else
215
+ repo="$(_proof_check_net gh repo view --json nameWithOwner -q .nameWithOwner 2>/dev/null || true)"
216
+ fi
202
217
 
203
218
  # --- Resolve head sha: PR head if a pr_url is given, else proof fallback. --
204
219
  local head_sha=""
package/autonomy/loki CHANGED
@@ -71,6 +71,13 @@ if [ -f "$_LOKI_SCRIPT_DIR/lib/lock.sh" ]; then
71
71
  source "$_LOKI_SCRIPT_DIR/lib/lock.sh"
72
72
  fi
73
73
 
74
+ # Guarded browser opener (loki_open_url). Every browser open goes through it
75
+ # so tests, CI and non-TTY runs never pop a tab. If missing, nothing opens.
76
+ if [ -f "$_LOKI_SCRIPT_DIR/lib/browser-open.sh" ]; then
77
+ # shellcheck source=lib/browser-open.sh
78
+ source "$_LOKI_SCRIPT_DIR/lib/browser-open.sh"
79
+ fi
80
+
74
81
  # Crash-reporting helpers (provides loki_collection_enabled, used by
75
82
  # cmd_telemetry status and cmd_crash). Self-guarded against double-source.
76
83
  if [ -f "$_LOKI_SCRIPT_DIR/crash.sh" ]; then
@@ -3613,8 +3620,8 @@ cmd_start() {
3613
3620
  # cockpit hint is printed by _loki_start_handoff for the Both path.
3614
3621
  args+=("--bg")
3615
3622
  local _du="http://127.0.0.1:${LOKI_DASHBOARD_PORT:-57374}"
3616
- if command -v open >/dev/null 2>&1; then open "$_du" >/dev/null 2>&1 &
3617
- elif command -v xdg-open >/dev/null 2>&1; then xdg-open "$_du" >/dev/null 2>&1 & fi
3623
+ # stdout must stay unredirected: loki_open_url checks the TTY.
3624
+ loki_open_url "$_du" 2>/dev/null &
3618
3625
  ;;
3619
3626
  launch-bg-watch)
3620
3627
  # Watch (cockpit only) detaches without opening the browser.
@@ -7693,16 +7700,10 @@ cmd_dashboard_open() {
7693
7700
 
7694
7701
  echo -e "${GREEN}Opening dashboard: $url${NC}"
7695
7702
 
7696
- # Open in browser (cross-platform)
7697
- if command -v open &> /dev/null; then
7698
- open "$url"
7699
- elif command -v xdg-open &> /dev/null; then
7700
- xdg-open "$url"
7701
- elif command -v start &> /dev/null; then
7702
- start "$url"
7703
- else
7703
+ # Open in browser (cross-platform, guarded: no-op in tests/CI/non-TTY)
7704
+ if ! loki_open_url "$url" 2>/dev/null; then
7704
7705
  echo ""
7705
- echo "Could not detect browser opener."
7706
+ echo "Browser not opened (headless, test, or no opener)."
7706
7707
  echo "Please open in browser: $url"
7707
7708
  fi
7708
7709
  }
@@ -7753,6 +7754,12 @@ cmd_welcome_terminal() {
7753
7754
  echo -e " ${DIM}Anonymous diagnostics on (os/arch/version/error type only -- never${NC}"
7754
7755
  echo -e " ${DIM}code, prompts, or paths). Off: loki telemetry off - docs/PRIVACY.md${NC}"
7755
7756
  echo ""
7757
+ elif [ -n "${LOKI_TELEMETRY_DISABLED:-}" ] || [ -n "${DO_NOT_TRACK:-}" ]; then
7758
+ # Off-by-default means silence otherwise (nothing to disclose), but an
7759
+ # EXPLICIT opt-out via env var deserves a confirmation that it took
7760
+ # effect, distinct from the default off state nobody asked about.
7761
+ echo -e " ${DIM}Analytics are off (LOKI_TELEMETRY_DISABLED/DO_NOT_TRACK).${NC}"
7762
+ echo ""
7756
7763
  fi
7757
7764
  }
7758
7765
 
@@ -7774,21 +7781,14 @@ cmd_welcome() {
7774
7781
  q="${q}&telemetry=off"
7775
7782
  fi
7776
7783
 
7777
- # Headless / Docker / CI / no-browser: print the terminal welcome only.
7778
- local has_browser=""
7779
- if command -v open >/dev/null 2>&1; then has_browser="open"
7780
- elif command -v xdg-open >/dev/null 2>&1; then has_browser="xdg-open"
7781
- elif command -v start >/dev/null 2>&1; then has_browser="start"
7782
- fi
7783
-
7784
- if [ -z "$has_browser" ] || [ ! -f "$welcome_file" ] || [ -n "${CI:-}" ] || [ ! -t 1 ]; then
7785
- cmd_welcome_terminal "$ver"
7786
- [ -f "$welcome_file" ] && echo -e " ${DIM}Full welcome page: ${welcome_file}${NC}" && echo ""
7784
+ # Headless / Docker / CI / tests / no-browser: print the terminal welcome.
7785
+ if [ -f "$welcome_file" ] && loki_open_url "file://${welcome_file}?${q}" 2>/dev/null; then
7786
+ echo -e "${GREEN}Opened the Loki Mode welcome page in your browser.${NC}"
7787
7787
  return 0
7788
7788
  fi
7789
-
7790
- echo -e "${GREEN}Opening the Loki Mode welcome page in your browser...${NC}"
7791
- "$has_browser" "file://${welcome_file}?${q}" >/dev/null 2>&1 || cmd_welcome_terminal "$ver"
7789
+ cmd_welcome_terminal "$ver"
7790
+ [ -f "$welcome_file" ] && echo -e " ${DIM}Full welcome page: ${welcome_file}${NC}" && echo ""
7791
+ return 0
7792
7792
  }
7793
7793
 
7794
7794
  # First-run hook: open the welcome once, then never again. Browser only when
@@ -7802,7 +7802,7 @@ cmd_welcome_maybe_firstrun() {
7802
7802
  # re-checks for a browser and falls back to the terminal welcome, so we
7803
7803
  # gate purely on interactivity + CI here (grouped to avoid &&/|| precedence
7804
7804
  # ambiguity). Backgrounded so it never delays the start.
7805
- if [ -t 1 ] && [ -z "${CI:-}" ]; then
7805
+ if declare -F loki_browser_allowed >/dev/null 2>&1 && loki_browser_allowed; then
7806
7806
  (cmd_welcome >/dev/null 2>&1 &) || true
7807
7807
  fi
7808
7808
  }
@@ -8132,11 +8132,7 @@ cmd_web_start() {
8132
8132
  local url="http://${PURPLE_LAB_DEFAULT_HOST}:${port}/lab/"
8133
8133
  echo -e "Open: ${CYAN}$url${NC}"
8134
8134
  if [ "$open_browser" = true ]; then
8135
- if command -v open &> /dev/null; then
8136
- open "$url"
8137
- elif command -v xdg-open &> /dev/null; then
8138
- xdg-open "$url"
8139
- fi
8135
+ loki_open_url "$url" 2>/dev/null || true
8140
8136
  fi
8141
8137
  return 0
8142
8138
  fi
@@ -8231,15 +8227,7 @@ cmd_web_start() {
8231
8227
  if [ "$open_browser" = true ]; then
8232
8228
  if [ "$server_ready" = true ]; then
8233
8229
  echo ""
8234
- if command -v open &> /dev/null; then
8235
- open "$url"
8236
- elif command -v xdg-open &> /dev/null; then
8237
- xdg-open "$url"
8238
- elif command -v start &> /dev/null; then
8239
- start "$url"
8240
- else
8241
- echo "Please open in browser: $url"
8242
- fi
8230
+ loki_open_url "$url" 2>/dev/null || echo "Please open in browser: $url"
8243
8231
  else
8244
8232
  echo ""
8245
8233
  echo -e "${YELLOW}Browser not opened because server is not ready yet.${NC}"
@@ -8885,15 +8873,7 @@ cmd_preview() {
8885
8873
  echo "Served from localhost on this machine."
8886
8874
 
8887
8875
  if [ "$open_browser" = true ]; then
8888
- if command -v open &> /dev/null; then
8889
- open "$url"
8890
- elif command -v xdg-open &> /dev/null; then
8891
- xdg-open "$url"
8892
- elif command -v start &> /dev/null; then
8893
- start "$url"
8894
- else
8895
- echo "Please open in browser: $url"
8896
- fi
8876
+ loki_open_url "$url" 2>/dev/null || echo "Please open in browser: $url"
8897
8877
  fi
8898
8878
  }
8899
8879
 
@@ -16050,16 +16030,16 @@ cmd_demo() {
16050
16030
  if [ "$has_web" = true ] && [ -n "$open_target" ]; then
16051
16031
  echo -e "${CYAN}A web frontend was generated.${NC}"
16052
16032
  echo ""
16053
- echo -n "Open in browser? [Y/n] "
16054
- read -r answer </dev/tty 2>/dev/null || answer="n"
16055
- if [[ -z "$answer" || "$answer" =~ ^[Yy] ]]; then
16056
- if command -v open &>/dev/null; then
16057
- open "$open_target"
16058
- elif command -v xdg-open &>/dev/null; then
16059
- xdg-open "$open_target"
16060
- else
16061
- echo -e "Open this file in your browser: ${BLUE}$open_target${NC}"
16062
- fi
16033
+ local answer="n"
16034
+ # Ask only when a browser may open, so a test never blocks on /dev/tty.
16035
+ if loki_browser_allowed 2>/dev/null; then
16036
+ echo -n "Open in browser? [Y/n] "
16037
+ read -r answer </dev/tty 2>/dev/null || answer="n"
16038
+ fi
16039
+ if [[ -z "$answer" || "$answer" =~ ^[Yy] ]] && loki_open_url "$open_target" 2>/dev/null; then
16040
+ :
16041
+ else
16042
+ echo -e "Open this file in your browser: ${BLUE}$open_target${NC}"
16063
16043
  fi
16064
16044
  elif [ "$has_start_script" = true ]; then
16065
16045
  echo -e "${CYAN}A start script was found in package.json.${NC}"
@@ -17958,9 +17938,7 @@ print(json.dumps({'id': manifest.id, 'dir': str(pipeline.migration_dir)}))
17958
17938
  sleep 2
17959
17939
  if kill -0 "$migrate_dashboard_pid" 2>/dev/null; then
17960
17940
  echo -e " Dashboard: ${CYAN}${url_scheme}://127.0.0.1:${dashboard_port}/${NC}"
17961
- if [[ "$OSTYPE" == "darwin"* ]]; then
17962
- open "${url_scheme}://127.0.0.1:${dashboard_port}/" 2>/dev/null || true
17963
- fi
17941
+ loki_open_url "${url_scheme}://127.0.0.1:${dashboard_port}/" 2>/dev/null || true
17964
17942
  echo ""
17965
17943
  else
17966
17944
  echo -e " ${YELLOW}Dashboard failed to start (check logs)${NC}"
@@ -37174,15 +37152,9 @@ print(json.dumps(doc, indent=2))
37174
37152
  exit 1
37175
37153
  fi
37176
37154
  echo -e "${GREEN}Opening proof: $html${NC}"
37177
- if command -v open &>/dev/null; then
37178
- open "$html"
37179
- elif command -v xdg-open &>/dev/null; then
37180
- xdg-open "$html"
37181
- elif command -v start &>/dev/null; then
37182
- start "$html"
37183
- else
37155
+ if ! loki_open_url "$html" 2>/dev/null; then
37184
37156
  echo ""
37185
- echo "Could not detect browser opener."
37157
+ echo "Browser not opened (headless, test, or no opener)."
37186
37158
  echo "Please open in browser: $html"
37187
37159
  fi
37188
37160
  exit 0