loki-mode 10.6.14 → 10.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -5
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/acp-agent.py +128 -0
- package/autonomy/docker-run.sh +8 -0
- package/autonomy/issue-parser.sh +7 -7
- package/autonomy/issue-providers.sh +159 -1
- package/autonomy/lib/engine10-push.sh +4 -1
- package/autonomy/lib/review_risk.py +91 -0
- package/autonomy/loki +209 -19
- package/autonomy/mobile-verify.sh +66 -0
- package/autonomy/run.sh +44 -2
- package/completions/_loki +2 -0
- package/completions/loki.bash +1 -1
- package/dashboard/__init__.py +1 -1
- package/dashboard/api_runs_v1.py +80 -0
- package/dashboard/server.py +2 -0
- package/docs/INSTALLATION.md +5 -1
- package/docs/acp-editors.md +29 -0
- package/docs/api-runs-v1.md +12 -0
- package/docs/dev/architecture-reference.md +4 -0
- package/docs/merge-queue.md +13 -0
- package/docs/mobile-verify.md +11 -0
- package/docs/sentry-intake.md +13 -0
- package/docs/v10/BOARD.md +31 -6
- package/docs/v10/CONTROL-PLANE.md +7 -0
- package/docs/v10/CP-ENTERPRISE-UI.md +276 -0
- package/docs/v10/D61-SPEED.md +4 -0
- package/docs/v10/DECISIONS.md +115 -0
- package/docs/v10/ENGINE-LAWS.md +155 -0
- package/docs/v10/FAILURE-CLASSES.md +145 -0
- package/docs/v10/FOUNDER-QUEUE.md +1 -0
- package/docs/v10/GAP-RESEARCH-1003.md +32 -0
- package/docs/v10/GUIDE.md +4 -2
- package/docs/v10/L0-WAVE1.md +81 -0
- package/docs/v10/MORNING-BRIEF-1003.md +25 -0
- package/docs/v10/OPERATING-MODEL.md +1 -0
- package/docs/v10/PROGRESS.md +99 -0
- package/docs/v10/SWARM.md +32 -0
- package/docs/visual-evidence.md +5 -0
- package/loki-ts/dist/loki.js +805 -777
- package/loki-ts/package.json +1 -0
- package/mcp/__init__.py +1 -1
- package/package.json +1 -1
- package/packages/control-plane/dist/server.js +549 -21
- package/packages/control-plane/drizzle/0002_tiresome_fixer.sql +7 -0
- package/packages/control-plane/drizzle/0003_integrity.sql +4 -0
- package/packages/control-plane/drizzle/meta/0002_snapshot.json +409 -0
- package/packages/control-plane/drizzle/meta/0003_snapshot.json +437 -0
- package/packages/control-plane/drizzle/meta/_journal.json +14 -0
- package/packages/control-plane/src/db/migrate.ts +6 -2
- package/packages/control-plane/src/db/prune.ts +91 -0
- package/packages/control-plane/src/db/schema.ts +13 -0
- package/packages/control-plane/src/server/actions.ts +60 -0
- package/packages/control-plane/src/server/app.ts +59 -4
- package/packages/control-plane/src/server/integrity.ts +147 -0
- package/packages/control-plane/src/server/runs.ts +46 -4
- package/packages/control-plane/src/shipper/discover.ts +32 -3
- package/packages/control-plane/src/shipper/ship.ts +15 -0
- package/packages/control-plane/src/shipper/watch.ts +66 -0
- package/packages/control-plane/ui/dist/assets/index-DHDI_dHM.css +1 -0
- package/packages/control-plane/ui/dist/assets/index-DaQr0MPS.js +129 -0
- package/packages/control-plane/ui/dist/index.html +3 -3
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
- package/web-app/dist/assets/{AdminPage-oAFMNfKH.js → AdminPage-BJSU5hY1.js} +1 -1
- package/web-app/dist/assets/{Avatar-BA2gnzyf.js → Avatar-CSOSpw9c.js} +1 -1
- package/web-app/dist/assets/{Badge-Dsluv8n1.js → Badge-COr7vj0D.js} +1 -1
- package/web-app/dist/assets/{Button-WmW8A-iG.js → Button-DGoWFERp.js} +1 -1
- package/web-app/dist/assets/{CockpitPage-BR5o_RgL.js → CockpitPage-CBpWkNXY.js} +1 -1
- package/web-app/dist/assets/{ComparePage-3Eg-pKvO.js → ComparePage-BwT8vqrL.js} +1 -1
- package/web-app/dist/assets/{ErrorBoundary-DAfrnnsQ.js → ErrorBoundary-D7SicDDA.js} +1 -1
- package/web-app/dist/assets/{EvidenceReceiptPanel-wLZI4OTr.js → EvidenceReceiptPanel-bx-5liG2.js} +1 -1
- package/web-app/dist/assets/{GitHubIssuesPanel-BOuadisf.js → GitHubIssuesPanel-BAeeWLqY.js} +1 -1
- package/web-app/dist/assets/{GitHubPRsPanel-BTVoVpB3.js → GitHubPRsPanel-CssD8lGI.js} +1 -1
- package/web-app/dist/assets/{HomePage-s3GWdNEe.js → HomePage-SL5PT3m5.js} +2 -2
- package/web-app/dist/assets/{LoginPage-BfOZ566t.js → LoginPage-C8hFOZOM.js} +1 -1
- package/web-app/dist/assets/{MagicPage-9EZ83xOt.js → MagicPage-xHbHkrPf.js} +1 -1
- package/web-app/dist/assets/{MetricsPage-OJMOYhHP.js → MetricsPage-B_2CKFcC.js} +1 -1
- package/web-app/dist/assets/{NotFoundPage-_LoJ6KEv.js → NotFoundPage-CXwXMq35.js} +1 -1
- package/web-app/dist/assets/{ProjectPage-Duj5LSt6.js → ProjectPage-AaisMQI-.js} +3 -3
- package/web-app/dist/assets/{ProjectsPage-BBud3JrO.js → ProjectsPage-Cej5ResX.js} +1 -1
- package/web-app/dist/assets/{SettingsPage-C4zdgKno.js → SettingsPage-DKv4mn9H.js} +1 -1
- package/web-app/dist/assets/{ShowcasePage-Nb3ACjMR.js → ShowcasePage-Dkiyzne4.js} +1 -1
- package/web-app/dist/assets/{SystemSettingsPage-BDi49TDh.js → SystemSettingsPage-DaBLQ5dU.js} +1 -1
- package/web-app/dist/assets/{TeamsPage-HkcqkjLa.js → TeamsPage-B2yUr31k.js} +1 -1
- package/web-app/dist/assets/{TemplatesPage-CK796Y1F.js → TemplatesPage-CfYiANhU.js} +1 -1
- package/web-app/dist/assets/{TerminalOutput-BkvdNuT6.js → TerminalOutput-CMXoexLM.js} +1 -1
- package/web-app/dist/assets/{activity-DFgMgETR.js → activity-B0dzoZtY.js} +1 -1
- package/web-app/dist/assets/{bell-BInGGOMZ.js → bell-DFOTaSoo.js} +1 -1
- package/web-app/dist/assets/{bot-D43Yjsj4.js → bot-Dz-WSQyP.js} +1 -1
- package/web-app/dist/assets/{check-DFAGTLM9.js → check-L1QIRueJ.js} +1 -1
- package/web-app/dist/assets/{chevron-left-Drwin7n1.js → chevron-left-1TrnIEZS.js} +1 -1
- package/web-app/dist/assets/{circle-alert-DLWdb7V9.js → circle-alert-8Bf6xEDx.js} +1 -1
- package/web-app/dist/assets/{clock-Dsbi59u5.js → clock-DB8NE6P5.js} +1 -1
- package/web-app/dist/assets/{cloud-CUU-dXTP.js → cloud-DUSL3wZS.js} +1 -1
- package/web-app/dist/assets/{code-xml-CqIh7Wui.js → code-xml-DbjryWNl.js} +1 -1
- package/web-app/dist/assets/{database-DASN5Bfh.js → database-DrezrMSs.js} +1 -1
- package/web-app/dist/assets/{dollar-sign-ZvXzAihc.js → dollar-sign-CGbaLMzR.js} +1 -1
- package/web-app/dist/assets/{file-code-corner-ED_gN-N8.js → file-code-corner-Dfypa-r4.js} +1 -1
- package/web-app/dist/assets/{file-plus-BmBHndXt.js → file-plus-CzV5L2Gd.js} +1 -1
- package/web-app/dist/assets/{globe-Ck_iEzO4.js → globe-BoYjTwPQ.js} +1 -1
- package/web-app/dist/assets/{hammer-B8YKMard.js → hammer-WO-HuiyJ.js} +1 -1
- package/web-app/dist/assets/{index-D7SxmjVQ.js → index-CjIOUjnd.js} +3 -3
- package/web-app/dist/assets/{layers-CGVsj3wB.js → layers-Bn6jtKNk.js} +1 -1
- package/web-app/dist/assets/{loader-circle-FIH7UJDF.js → loader-circle-gERWRnmW.js} +1 -1
- package/web-app/dist/assets/{lock-vR8HoAnK.js → lock-BIiMLJq8.js} +1 -1
- package/web-app/dist/assets/{package-mZ6LFwXp.js → package-dLvDSVys.js} +1 -1
- package/web-app/dist/assets/{plus-CtnSVu0w.js → plus-BHM3ZLhZ.js} +1 -1
- package/web-app/dist/assets/{refresh-cw-BIapF6Gj.js → refresh-cw-BAp_alv6.js} +1 -1
- package/web-app/dist/assets/{rotate-ccw-LMkSsm21.js → rotate-ccw-CBhwq_MG.js} +1 -1
- package/web-app/dist/assets/{scroll-text-DQVlIhr9.js → scroll-text-DoS-6dfU.js} +1 -1
- package/web-app/dist/assets/{server-GqyhQxmz.js → server-5JgdDWQO.js} +1 -1
- package/web-app/dist/assets/{shield-alert-BrFTNJ-I.js → shield-alert-DjmlLT34.js} +1 -1
- package/web-app/dist/assets/{thumbs-up-lgwAEJFW.js → thumbs-up-BtiEXl3p.js} +1 -1
- package/web-app/dist/assets/{trash-2-C1cNysK4.js → trash-2-ClsWU_Vg.js} +1 -1
- package/web-app/dist/assets/{trending-down-DQFZ6j5s.js → trending-down-DPS4M7_X.js} +1 -1
- package/web-app/dist/assets/{trending-up-BJG_Zo8V.js → trending-up-D6OJ38-V.js} +1 -1
- package/web-app/dist/assets/{upload-BOY1aTpA.js → upload-DOCzLUpF.js} +1 -1
- package/web-app/dist/assets/{usePolling-DoAmsTNy.js → usePolling-CljQTWj_.js} +1 -1
- package/web-app/dist/assets/{user-iOfCe0_v.js → user-C0G82Weu.js} +1 -1
- package/web-app/dist/index.html +1 -1
- package/packages/control-plane/ui/dist/assets/index-CFfrqCx2.css +0 -1
- package/packages/control-plane/ui/dist/assets/index-Jt31nrK-.js +0 -114
package/autonomy/loki
CHANGED
|
@@ -1693,6 +1693,7 @@ show_help() {
|
|
|
1693
1693
|
echo "Observe:"
|
|
1694
1694
|
echo " dashboard [cmd] Operations UI server (start|stop|status|url|open)"
|
|
1695
1695
|
echo " mcp Launch the MCP server (Model Context Protocol, stdio)"
|
|
1696
|
+
echo " acp Run Loki as an ACP agent over stdio (VS Code, JetBrains, Zed)"
|
|
1696
1697
|
echo ""
|
|
1697
1698
|
echo "Report:"
|
|
1698
1699
|
echo " report [cmd] Reporting: session|metrics|cost|export|share|dogfood"
|
|
@@ -1812,7 +1813,7 @@ show_help() {
|
|
|
1812
1813
|
# missing from this list, so a new command cannot ship invisible.
|
|
1813
1814
|
echo ""
|
|
1814
1815
|
echo "All commands:"
|
|
1815
|
-
echo " agent analyze api assets audit backlog bench checkpoint (cp) ci cleanup"
|
|
1816
|
+
echo " acp agent analyze api assets audit backlog bench checkpoint (cp) ci cleanup"
|
|
1816
1817
|
echo " cluster cockpit code compliance completions compound config context (ctx)"
|
|
1817
1818
|
echo " cost council crash dashboard demo deploy docker docs doctor dogfood"
|
|
1818
1819
|
echo " enterprise estimate explain export failover github grill heal help"
|
|
@@ -1820,7 +1821,7 @@ show_help() {
|
|
|
1820
1821
|
echo " issue keys kpis logs magic mcp memory metrics migrate modernize monitor"
|
|
1821
1822
|
echo " next notify onboard open optimize otel outcomes own (handoff) pause plan preview"
|
|
1822
1823
|
echo " projects proof (receipt) provider quick quickstart rc remote report reset"
|
|
1823
|
-
echo " resume review rollback run sandbox secrets secure self-update sentrux"
|
|
1824
|
+
echo " merge resume review rollback run sandbox secrets secure self-update sentrux"
|
|
1824
1825
|
echo " serve setup-skill share ship spec start state stats status steer stop"
|
|
1825
1826
|
echo " syslog telemetry template test tour trigger trust trust-metrics"
|
|
1826
1827
|
echo " ultracode update verify version voice watch watchdog web welcome why"
|
|
@@ -2007,6 +2008,9 @@ _loki_control_ui() {
|
|
|
2007
2008
|
|
|
2008
2009
|
cmd_ui() {
|
|
2009
2010
|
_loki_control_ui "$@" && return 0
|
|
2011
|
+
# CP-LEGACY: callers that must never start the classic server (run.sh
|
|
2012
|
+
# auto-open) set LOKI_UI_NO_CLASSIC=1.
|
|
2013
|
+
[ "${LOKI_UI_NO_CLASSIC:-0}" = "1" ] && return 1
|
|
2010
2014
|
local port="" pid=""
|
|
2011
2015
|
if [ -f "$DASHBOARD_PID_FILE" ]; then
|
|
2012
2016
|
pid=$(cat "$DASHBOARD_PID_FILE" 2>/dev/null)
|
|
@@ -2084,6 +2088,18 @@ detect_arg_type() {
|
|
|
2084
2088
|
return 0
|
|
2085
2089
|
fi
|
|
2086
2090
|
|
|
2091
|
+
# Sentry intake: an https Sentry issue URL, or an exported Sentry JSON file
|
|
2092
|
+
# on disk, is an issue (checked before the .json -> prd rule below).
|
|
2093
|
+
if [[ "$arg" =~ ^https://[^/]*sentry[^/]*/.*issues/[0-9]+ ]]; then
|
|
2094
|
+
echo "issue"
|
|
2095
|
+
return 0
|
|
2096
|
+
fi
|
|
2097
|
+
if [[ "$arg" == *.json ]] && [ -f "$arg" ] \
|
|
2098
|
+
&& head -c 2000000 "$arg" 2>/dev/null | grep -Eq '"(shortId|culprit)"[[:space:]]*:|"exception"[[:space:]]*:|"type"[[:space:]]*:[[:space:]]*"exception"'; then
|
|
2099
|
+
echo "issue"
|
|
2100
|
+
return 0
|
|
2101
|
+
fi
|
|
2102
|
+
|
|
2087
2103
|
# Known PRD extensions - treat as PRD first (highest priority).
|
|
2088
2104
|
# This handles edge case where a file is literally named "123.md" or
|
|
2089
2105
|
# sits in a dir that matches another pattern.
|
|
@@ -7441,14 +7457,28 @@ _loki_dashboard_bind_is_safe() {
|
|
|
7441
7457
|
return 1
|
|
7442
7458
|
}
|
|
7443
7459
|
|
|
7460
|
+
_loki_dashboard_args_need_classic() {
|
|
7461
|
+
local a
|
|
7462
|
+
for a in "$@"; do
|
|
7463
|
+
case "$a" in
|
|
7464
|
+
--host|--host=*|--tls-cert|--tls-cert=*|--tls-key|--tls-key=*) return 0 ;;
|
|
7465
|
+
esac
|
|
7466
|
+
done
|
|
7467
|
+
return 1
|
|
7468
|
+
}
|
|
7469
|
+
|
|
7444
7470
|
cmd_dashboard() {
|
|
7445
7471
|
local subcommand="${1:-}"
|
|
7446
7472
|
|
|
7447
|
-
#
|
|
7448
|
-
#
|
|
7473
|
+
# CP-LEGACY (D57): the Control Plane is the default UI for `dashboard`,
|
|
7474
|
+
# `dashboard start` and `dashboard open`, with or without flags. The legacy
|
|
7475
|
+
# Python dashboard stays reachable only by explicit opt-in:
|
|
7476
|
+
# LOKI_LEGACY_DASHBOARD=1 or LOKI_CONTROL_DEFAULT=0. Enterprise bind/TLS
|
|
7477
|
+
# flags (--host, --tls-cert, --tls-key) are classic-server concepts and
|
|
7478
|
+
# keep the classic server.
|
|
7449
7479
|
case "$subcommand" in
|
|
7450
7480
|
""|start|open)
|
|
7451
|
-
if [
|
|
7481
|
+
if [ "${LOKI_LEGACY_DASHBOARD:-0}" != "1" ] && ! _loki_dashboard_args_need_classic "${@:2}"; then
|
|
7452
7482
|
_loki_control_ui "${@:2}" && return 0
|
|
7453
7483
|
fi
|
|
7454
7484
|
;;
|
|
@@ -8307,6 +8337,11 @@ cmd_web_redirect_to_dashboard() {
|
|
|
8307
8337
|
echo " it watches runs that are already going. To build from that spec:" >&2
|
|
8308
8338
|
echo " loki start '${_web_dropped_prd}'" >&2
|
|
8309
8339
|
fi
|
|
8340
|
+
# Flags such as --port must reach the classic validator, so only the
|
|
8341
|
+
# bare form (optionally --no-open) is routed to the Control Plane.
|
|
8342
|
+
if [ "${LOKI_LEGACY_DASHBOARD:-0}" != "1" ] && [ ${#dash_args[@]} -eq 0 ] && _loki_control_ui; then
|
|
8343
|
+
return 0
|
|
8344
|
+
fi
|
|
8310
8345
|
cmd_dashboard_start ${dash_args[@]+"${dash_args[@]}"}
|
|
8311
8346
|
}
|
|
8312
8347
|
|
|
@@ -21764,8 +21799,10 @@ ultracode council demo dogfood heal modernize import issue github init template
|
|
|
21764
21799
|
status stop pause resume monitor watch watchdog dashboard web open preview share \
|
|
21765
21800
|
memory context ctx checkpoint state report kpis cost metrics stats logs otel \
|
|
21766
21801
|
syslog telemetry crash config provider doctor setup-skill onboard quickstart \
|
|
21767
|
-
welcome update self-update rollback failover cluster enterprise remote deploy docker \
|
|
21802
|
+
welcome update self-update rollback merge failover cluster enterprise remote deploy docker \
|
|
21768
21803
|
sandbox api mcp magic compound assets export wiki control docs explain why audit compliance \
|
|
21804
|
+
welcome update self-update rollback failover cluster enterprise remote deploy docker \
|
|
21805
|
+
sandbox api mcp acp magic compound assets export wiki control docs explain why audit compliance \
|
|
21769
21806
|
secrets analyze optimize bench ci reset cleanup notify trigger voice sentrux \
|
|
21770
21807
|
worktree wt projects cp rc trust-metrics serve agent code self_update test help version"
|
|
21771
21808
|
local best
|
|
@@ -22097,6 +22134,9 @@ main() {
|
|
|
22097
22134
|
rollback)
|
|
22098
22135
|
cmd_rollback "$@"
|
|
22099
22136
|
;;
|
|
22137
|
+
merge)
|
|
22138
|
+
cmd_merge "$@"
|
|
22139
|
+
;;
|
|
22100
22140
|
council)
|
|
22101
22141
|
cmd_council "$@"
|
|
22102
22142
|
;;
|
|
@@ -22177,6 +22217,10 @@ main() {
|
|
|
22177
22217
|
mcp)
|
|
22178
22218
|
cmd_mcp "$@"
|
|
22179
22219
|
;;
|
|
22220
|
+
acp)
|
|
22221
|
+
# ACP agent over stdio; stdout is protocol-only. See docs/acp-editors.md.
|
|
22222
|
+
LOKI_ACP_LOKI="${LOKI_ACP_LOKI:-$_LOKI_SCRIPT_DIR/loki}" exec python3 "$_LOKI_SCRIPT_DIR/acp-agent.py"
|
|
22223
|
+
;;
|
|
22180
22224
|
migrate)
|
|
22181
22225
|
# CLI consolidation (Phase B): 'migrate' -> 'modernize migrate'.
|
|
22182
22226
|
_deprecated_alias migrate "modernize migrate" "$@"
|
|
@@ -22709,6 +22753,7 @@ cmd_review() {
|
|
|
22709
22753
|
local review_ultra=false
|
|
22710
22754
|
local review_assume_yes=false
|
|
22711
22755
|
local review_timeout=""
|
|
22756
|
+
local review_risk=false
|
|
22712
22757
|
|
|
22713
22758
|
while [[ $# -gt 0 ]]; do
|
|
22714
22759
|
case "$1" in
|
|
@@ -22736,6 +22781,10 @@ cmd_review() {
|
|
|
22736
22781
|
echo " skip the prompt. Optional [file-or-dir] is"
|
|
22737
22782
|
echo " passed as the ultrareview target (branch / PR"
|
|
22738
22783
|
echo " number / base branch)."
|
|
22784
|
+
echo " --risk Deterministic 0-100 diff risk score with per-factor"
|
|
22785
|
+
echo " breakdown (files, sensitive paths, size, test delta,"
|
|
22786
|
+
echo " deleted tests). Works with --pr/--staged/--since;"
|
|
22787
|
+
echo " 'loki review <pr-number> --risk' also works. Use --json"
|
|
22739
22788
|
echo " --yes, -y Skip the --ultra confirmation prompt"
|
|
22740
22789
|
echo " --timeout <minutes> --ultra: max minutes to wait (default 30)"
|
|
22741
22790
|
echo " --help, -h Show this help"
|
|
@@ -22759,6 +22808,8 @@ cmd_review() {
|
|
|
22759
22808
|
return 0
|
|
22760
22809
|
;;
|
|
22761
22810
|
--ultra) review_ultra=true; shift ;;
|
|
22811
|
+
--risk) review_risk=true; shift ;;
|
|
22812
|
+
--json) review_format="json"; shift ;;
|
|
22762
22813
|
--yes|-y) review_assume_yes=true; shift ;;
|
|
22763
22814
|
--timeout)
|
|
22764
22815
|
shift
|
|
@@ -22814,6 +22865,29 @@ cmd_review() {
|
|
|
22814
22865
|
return $?
|
|
22815
22866
|
fi
|
|
22816
22867
|
|
|
22868
|
+
if [ "$review_risk" = true ]; then
|
|
22869
|
+
if [ -z "$review_pr" ] && [ -z "$review_since" ] && [[ "$review_target" =~ ^[0-9]+$ ]] && [ ! -e "$review_target" ]; then
|
|
22870
|
+
review_pr="$review_target"
|
|
22871
|
+
fi
|
|
22872
|
+
local risk_diff risk_src="uncommitted changes"
|
|
22873
|
+
if [ -n "$review_pr" ]; then
|
|
22874
|
+
command -v gh &>/dev/null || { echo "Error: gh CLI required for PR risk" >&2; return 1; }
|
|
22875
|
+
risk_diff=$(gh pr diff "$review_pr" 2>&1) || { echo "Error: Could not fetch PR #${review_pr}: $risk_diff" >&2; return 1; }
|
|
22876
|
+
risk_src="PR #${review_pr}"
|
|
22877
|
+
elif [ -n "$review_since" ]; then
|
|
22878
|
+
risk_diff=$(git diff "$review_since" 2>&1) || { echo "Error: Invalid commit reference: $review_since" >&2; return 1; }
|
|
22879
|
+
risk_src="changes since $review_since"
|
|
22880
|
+
elif [ "$review_staged" = true ]; then
|
|
22881
|
+
risk_diff=$(git diff --cached 2>/dev/null); risk_src="staged changes"
|
|
22882
|
+
else
|
|
22883
|
+
risk_diff=$(git diff 2>/dev/null)
|
|
22884
|
+
fi
|
|
22885
|
+
local risk_args=()
|
|
22886
|
+
[ "$review_format" = "json" ] && risk_args+=(--json)
|
|
22887
|
+
printf '%s\n' "$risk_diff" | python3 "$_LOKI_SCRIPT_DIR/lib/review_risk.py" ${risk_args[@]+"${risk_args[@]}"} "$risk_src"
|
|
22888
|
+
return $?
|
|
22889
|
+
fi
|
|
22890
|
+
|
|
22817
22891
|
# Severity level ordering (higher number = more severe)
|
|
22818
22892
|
_review_sev_level() {
|
|
22819
22893
|
case "$1" in
|
|
@@ -26294,6 +26368,93 @@ SHOW_EOF
|
|
|
26294
26368
|
esac
|
|
26295
26369
|
}
|
|
26296
26370
|
|
|
26371
|
+
# loki merge: serial PR merge queue. Queue lives in .loki/merge-queue (one PR
|
|
26372
|
+
# number per line). A PR merges only when `gh pr checks` exits 0 (all green);
|
|
26373
|
+
# after each merge the next PR is rebased onto the new base (gh pr update-branch
|
|
26374
|
+
# --rebase) and must go green again before it merges. Never forces anything.
|
|
26375
|
+
cmd_merge() {
|
|
26376
|
+
local sub="${1:-help}"
|
|
26377
|
+
shift 2>/dev/null || true
|
|
26378
|
+
local qfile=".loki/merge-queue"
|
|
26379
|
+
case "$sub" in
|
|
26380
|
+
add)
|
|
26381
|
+
[ $# -gt 0 ] || { echo "Usage: loki merge add <pr-number>..." >&2; return 2; }
|
|
26382
|
+
mkdir -p .loki || return 1
|
|
26383
|
+
local n
|
|
26384
|
+
for n in "$@"; do
|
|
26385
|
+
case "$n" in ''|*[!0-9]*) echo "Invalid PR number: $n" >&2; return 2 ;; esac
|
|
26386
|
+
if [ -f "$qfile" ] && grep -qx "$n" "$qfile"; then
|
|
26387
|
+
echo "PR #$n already queued"
|
|
26388
|
+
else
|
|
26389
|
+
echo "$n" >> "$qfile"
|
|
26390
|
+
echo "Queued PR #$n"
|
|
26391
|
+
fi
|
|
26392
|
+
done
|
|
26393
|
+
;;
|
|
26394
|
+
list|ls)
|
|
26395
|
+
if [ -s "$qfile" ]; then cat "$qfile" | sed 's/^/#/'; else echo "Merge queue is empty"; fi
|
|
26396
|
+
;;
|
|
26397
|
+
clear)
|
|
26398
|
+
rm -f "$qfile"; echo "Merge queue cleared"
|
|
26399
|
+
;;
|
|
26400
|
+
run)
|
|
26401
|
+
local dry=false
|
|
26402
|
+
[ "${1:-}" = "--dry-run" ] && dry=true
|
|
26403
|
+
if [ ! -s "$qfile" ]; then echo "Merge queue is empty"; return 0; fi
|
|
26404
|
+
command -v gh >/dev/null 2>&1 || { echo "gh CLI is required" >&2; return 1; }
|
|
26405
|
+
local poll="${LOKI_MERGE_POLL_S:-20}" tries="${LOKI_MERGE_MAX_POLLS:-90}"
|
|
26406
|
+
local first=true n rc i remaining="" merged=0 blocked=0
|
|
26407
|
+
while IFS= read -r n; do
|
|
26408
|
+
[ -n "$n" ] || continue
|
|
26409
|
+
if $dry; then
|
|
26410
|
+
rc=0; gh pr checks "$n" >/dev/null 2>&1 || rc=$?
|
|
26411
|
+
case "$rc" in
|
|
26412
|
+
0) echo "#$n: checks green, would merge (squash)" ;;
|
|
26413
|
+
8) echo "#$n: checks pending, would wait" ;;
|
|
26414
|
+
*) echo "#$n: checks not green, would skip" ;;
|
|
26415
|
+
esac
|
|
26416
|
+
$first || echo "#$n: would rebase onto new base first"
|
|
26417
|
+
first=false
|
|
26418
|
+
continue
|
|
26419
|
+
fi
|
|
26420
|
+
if ! $first; then
|
|
26421
|
+
gh pr update-branch "$n" --rebase >/dev/null 2>&1 \
|
|
26422
|
+
|| { echo "#$n: rebase onto new base failed, left in queue"; remaining="$remaining$n"$'\n'; blocked=$((blocked+1)); continue; }
|
|
26423
|
+
echo "#$n: rebased onto new base"
|
|
26424
|
+
fi
|
|
26425
|
+
rc=8; i=0
|
|
26426
|
+
while [ "$rc" = "8" ] && [ "$i" -lt "$tries" ]; do
|
|
26427
|
+
rc=0; gh pr checks "$n" >/dev/null 2>&1 || rc=$?
|
|
26428
|
+
[ "$rc" = "8" ] && { sleep "$poll"; i=$((i+1)); }
|
|
26429
|
+
done
|
|
26430
|
+
if [ "$rc" != "0" ]; then
|
|
26431
|
+
echo "#$n: checks not green (rc=$rc), left in queue, not merged"
|
|
26432
|
+
remaining="$remaining$n"$'\n'; blocked=$((blocked+1)); continue
|
|
26433
|
+
fi
|
|
26434
|
+
if gh pr merge "$n" --squash >/dev/null 2>&1; then
|
|
26435
|
+
echo "#$n: merged"; merged=$((merged+1)); first=false
|
|
26436
|
+
else
|
|
26437
|
+
echo "#$n: merge failed, left in queue"
|
|
26438
|
+
remaining="$remaining$n"$'\n'; blocked=$((blocked+1))
|
|
26439
|
+
fi
|
|
26440
|
+
done < "$qfile"
|
|
26441
|
+
if $dry; then echo "Dry run: nothing changed"; return 0; fi
|
|
26442
|
+
printf '%s' "$remaining" > "$qfile"
|
|
26443
|
+
echo "Merged: $merged, left in queue: $blocked"
|
|
26444
|
+
[ "$blocked" -eq 0 ]
|
|
26445
|
+
;;
|
|
26446
|
+
help|--help|-h|*)
|
|
26447
|
+
echo "Usage: loki merge <add|list|run|clear>"
|
|
26448
|
+
echo " add <pr>... Queue pull requests"
|
|
26449
|
+
echo " list Show the queue"
|
|
26450
|
+
echo " run [--dry-run] Merge one at a time, only when checks are green;"
|
|
26451
|
+
echo " each next PR is rebased onto the new base first."
|
|
26452
|
+
echo " --dry-run lists what would happen, changes nothing."
|
|
26453
|
+
[ "$sub" = "help" ] || [ "$sub" = "--help" ] || [ "$sub" = "-h" ] || return 2
|
|
26454
|
+
;;
|
|
26455
|
+
esac
|
|
26456
|
+
}
|
|
26457
|
+
|
|
26297
26458
|
# R6: one-command rollback. Top-level, obvious entry point that mirrors the Bun
|
|
26298
26459
|
# `loki rollback` subcommands (list/show/to/latest) so both routes are at parity.
|
|
26299
26460
|
# Restore is destructive on .loki/ state, so it ALWAYS captures a forced
|
|
@@ -38109,18 +38270,28 @@ cmd_docker() {
|
|
|
38109
38270
|
# the port-in-use check, so the picked port is ignored on reuse). Run in
|
|
38110
38271
|
# a subshell so its internal `exit` paths (already-running -> exit 0,
|
|
38111
38272
|
# errors -> exit 1) never abort the blocking docker run.
|
|
38112
|
-
|
|
38113
|
-
|
|
38114
|
-
|
|
38115
|
-
|
|
38116
|
-
|
|
38273
|
+
# CP-LEGACY (D57): open the Control Plane, never the classic server,
|
|
38274
|
+
# unless LOKI_LEGACY_DASHBOARD=1 opts back in. Same open gate as before.
|
|
38275
|
+
if [ "${LOKI_LEGACY_DASHBOARD:-0}" != "1" ]; then
|
|
38276
|
+
local _cp_args=()
|
|
38277
|
+
if [ ! -t 1 ] || [ "$_docker_bg" = "1" ] || [ "${LOKI_NO_AUTO_OPEN:-0}" = "1" ]; then
|
|
38278
|
+
_cp_args=(--no-open)
|
|
38279
|
+
fi
|
|
38280
|
+
( _loki_control_ui ${_cp_args[@]+"${_cp_args[@]}"} ) >&2 || true
|
|
38281
|
+
else
|
|
38282
|
+
local _dash_port="57374"
|
|
38283
|
+
if declare -F loki_docker_pick_host_port >/dev/null 2>&1; then
|
|
38284
|
+
_dash_port="$(loki_docker_pick_host_port 2>/dev/null || echo 57374)"
|
|
38285
|
+
fi
|
|
38286
|
+
( cmd_dashboard_start --port "$_dash_port" ) || true
|
|
38117
38287
|
|
|
38118
|
-
|
|
38119
|
-
|
|
38120
|
-
|
|
38121
|
-
|
|
38122
|
-
|
|
38123
|
-
|
|
38288
|
+
# Auto-open the dashboard in the browser, gated EXACTLY like
|
|
38289
|
+
# run.sh:~10088: a TTY on stdout, not background, and not opted out via
|
|
38290
|
+
# LOKI_NO_AUTO_OPEN=1. cmd_dashboard_open reads the persisted port file,
|
|
38291
|
+
# so the URL is correct even when an existing dashboard was reused.
|
|
38292
|
+
if [ -t 1 ] && [ "$_docker_bg" != "1" ] && [ "${LOKI_NO_AUTO_OPEN:-0}" != "1" ]; then
|
|
38293
|
+
( cmd_dashboard_open ) || true
|
|
38294
|
+
fi
|
|
38124
38295
|
fi
|
|
38125
38296
|
|
|
38126
38297
|
# F4 FIX-DOCKER-STOP (write side): record this run's container/state so
|
|
@@ -38139,8 +38310,27 @@ cmd_docker() {
|
|
|
38139
38310
|
# its live status from the bind-mounted .loki/session.json. Best-effort and
|
|
38140
38311
|
# failure-swallowed: registry bookkeeping must never block a build.
|
|
38141
38312
|
_loki_docker_register_host running
|
|
38142
|
-
"
|
|
38143
|
-
|
|
38313
|
+
local rc _errlog=""
|
|
38314
|
+
# Capture output for a failure summary only when no TTY is attached (a TTY
|
|
38315
|
+
# run already shows the live output and must not be piped).
|
|
38316
|
+
if [ ! -t 1 ] || [ ! -t 0 ]; then
|
|
38317
|
+
_errlog="$(mktemp "${TMPDIR:-/tmp}/loki-docker-out.XXXXXX" 2>/dev/null)" || _errlog=""
|
|
38318
|
+
fi
|
|
38319
|
+
if [ -n "$_errlog" ]; then
|
|
38320
|
+
"${docker_argv[@]}" 2>&1 | tee "$_errlog"
|
|
38321
|
+
rc=${PIPESTATUS[0]}
|
|
38322
|
+
else
|
|
38323
|
+
"${docker_argv[@]}"
|
|
38324
|
+
rc=$?
|
|
38325
|
+
fi
|
|
38326
|
+
if [ "$rc" -ne 0 ]; then
|
|
38327
|
+
local _last=""
|
|
38328
|
+
[ -n "$_errlog" ] && _last="$(grep -v '^[[:space:]]*$' "$_errlog" 2>/dev/null | tail -n 1)"
|
|
38329
|
+
echo -e "${RED}loki docker: container exited with code ${rc}.${NC}" >&2
|
|
38330
|
+
[ -n "$_last" ] && echo "Last error: ${_last}" >&2
|
|
38331
|
+
echo "Fix hint: for PRs set GH_TOKEN=\$(gh auth token), and ensure ANTHROPIC_API_KEY or a Claude login is available, then rerun." >&2
|
|
38332
|
+
fi
|
|
38333
|
+
[ -n "$_errlog" ] && rm -f "$_errlog" 2>/dev/null
|
|
38144
38334
|
_loki_docker_register_host stopped
|
|
38145
38335
|
# F4 FIX-DOCKER-STOP (clear side): runs unconditionally after the blocking
|
|
38146
38336
|
# call (not gated on rc), same as register_host stopped.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Mobile emulator tests: run a mobile project's tests on an available emulator
|
|
3
|
+
# or simulator. Never reports PASS without a run.
|
|
4
|
+
# Usage: mobile-verify.sh [project_dir]
|
|
5
|
+
# Env: LOKI_MOBILE_TEST_CMD override the test command
|
|
6
|
+
# LOKI_MOBILE_TIMEOUT seconds (default 600)
|
|
7
|
+
# Exit: 0 = ran and passed, or not a mobile project (stated)
|
|
8
|
+
# 1 = ran and failed
|
|
9
|
+
# 3 = NOT VERIFIED (no emulator/simulator, or no runnable test command)
|
|
10
|
+
set -u
|
|
11
|
+
dir="${1:-.}"
|
|
12
|
+
cd "$dir" || { echo "mobile tests: NOT VERIFIED (cannot enter $dir)"; exit 3; }
|
|
13
|
+
|
|
14
|
+
kind=""
|
|
15
|
+
if [ -f pubspec.yaml ] && grep -q "flutter" pubspec.yaml 2>/dev/null; then
|
|
16
|
+
kind="flutter"
|
|
17
|
+
elif [ -f package.json ] && grep -qE '"(expo|react-native)"[[:space:]]*:' package.json 2>/dev/null; then
|
|
18
|
+
kind="react-native"
|
|
19
|
+
fi
|
|
20
|
+
if [ -z "$kind" ]; then
|
|
21
|
+
echo "mobile tests: not applicable (no React Native, Expo or Flutter project)"
|
|
22
|
+
exit 0
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
device=""
|
|
26
|
+
if command -v adb >/dev/null 2>&1; then
|
|
27
|
+
d="$(adb devices 2>/dev/null | awk 'NR>1 && $2=="device" {print $1; exit}')"
|
|
28
|
+
[ -n "$d" ] && device="android:$d"
|
|
29
|
+
fi
|
|
30
|
+
if [ -z "$device" ] && command -v xcrun >/dev/null 2>&1; then
|
|
31
|
+
d="$(xcrun simctl list devices booted 2>/dev/null | awk '/\(Booted\)/ {print "ios-simulator"; exit}')"
|
|
32
|
+
[ -n "$d" ] && device="$d"
|
|
33
|
+
fi
|
|
34
|
+
if [ -z "$device" ]; then
|
|
35
|
+
echo "mobile tests: NOT VERIFIED ($kind project, no booted Android emulator or iOS simulator found; tests were not run)"
|
|
36
|
+
exit 3
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
cmd="${LOKI_MOBILE_TEST_CMD:-}"
|
|
40
|
+
if [ -z "$cmd" ]; then
|
|
41
|
+
if [ "$kind" = "flutter" ]; then
|
|
42
|
+
if [ -d integration_test ]; then cmd="flutter test integration_test"; else cmd="flutter test"; fi
|
|
43
|
+
elif grep -q '"test:e2e"' package.json 2>/dev/null; then
|
|
44
|
+
cmd="npm run test:e2e"
|
|
45
|
+
elif grep -q '"e2e"' package.json 2>/dev/null; then
|
|
46
|
+
cmd="npm run e2e"
|
|
47
|
+
fi
|
|
48
|
+
fi
|
|
49
|
+
if [ -z "$cmd" ]; then
|
|
50
|
+
echo "mobile tests: NOT VERIFIED ($kind project on $device, but no e2e test script (test:e2e or e2e) to run)"
|
|
51
|
+
exit 3
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
timeout_s="${LOKI_MOBILE_TIMEOUT:-600}"
|
|
55
|
+
if command -v timeout >/dev/null 2>&1; then
|
|
56
|
+
timeout -k 10 "$timeout_s" bash -c "$cmd"
|
|
57
|
+
else
|
|
58
|
+
bash -c "$cmd"
|
|
59
|
+
fi
|
|
60
|
+
rc=$?
|
|
61
|
+
if [ "$rc" -eq 0 ]; then
|
|
62
|
+
echo "mobile tests: PASS ($kind on $device, ran: $cmd)"
|
|
63
|
+
exit 0
|
|
64
|
+
fi
|
|
65
|
+
echo "mobile tests: FAIL ($kind on $device, ran: $cmd, exit $rc)"
|
|
66
|
+
exit 1
|
package/autonomy/run.sh
CHANGED
|
@@ -11804,6 +11804,26 @@ result = {
|
|
|
11804
11804
|
"successes": filter_relevant(successes, context)
|
|
11805
11805
|
}
|
|
11806
11806
|
|
|
11807
|
+
# Project memory: learnings and decisions persisted by earlier runs in THIS
|
|
11808
|
+
# project, newest first, bounded by entry count and total characters.
|
|
11809
|
+
if os.environ.get("LOKI_PROJECT_MEMORY", "1") != "0":
|
|
11810
|
+
try:
|
|
11811
|
+
max_chars = int(os.environ.get("LOKI_PROJECT_MEMORY_MAX_CHARS", "2000"))
|
|
11812
|
+
except ValueError:
|
|
11813
|
+
max_chars = 2000
|
|
11814
|
+
mem, used = [], 0
|
|
11815
|
+
for kind in ("mistakes", "patterns", "successes"):
|
|
11816
|
+
for e in reversed(load_jsonl(f".loki/memory/learnings/project-{kind}.jsonl")):
|
|
11817
|
+
d = e.get("description", "").strip()[:300]
|
|
11818
|
+
if not d or used + len(d) > max_chars:
|
|
11819
|
+
continue
|
|
11820
|
+
used += len(d)
|
|
11821
|
+
mem.append({"kind": kind[:-1] if kind != "successes" else "success", "description": d})
|
|
11822
|
+
if len(mem) >= 15:
|
|
11823
|
+
break
|
|
11824
|
+
if mem:
|
|
11825
|
+
result["project_memory"] = mem
|
|
11826
|
+
|
|
11807
11827
|
with open(".loki/state/relevant-learnings.json", 'w') as f:
|
|
11808
11828
|
json.dump(result, f, indent=2)
|
|
11809
11829
|
LEARNINGS_SCRIPT
|
|
@@ -11863,7 +11883,23 @@ def get_existing_hashes(filepath):
|
|
|
11863
11883
|
return hashes
|
|
11864
11884
|
|
|
11865
11885
|
def save_entries(filepath, entries, category):
|
|
11866
|
-
"""Save entries avoiding duplicates (case-insensitive)
|
|
11886
|
+
"""Save entries avoiding duplicates (case-insensitive).
|
|
11887
|
+
|
|
11888
|
+
Also mirrors into the project-scoped memory (.loki/memory/learnings/
|
|
11889
|
+
project-<kind>.jsonl) so the next run in this project reads them back.
|
|
11890
|
+
"""
|
|
11891
|
+
if os.environ.get("LOKI_PROJECT_MEMORY", "1") != "0":
|
|
11892
|
+
try:
|
|
11893
|
+
pdir = ".loki/memory/learnings"
|
|
11894
|
+
os.makedirs(pdir, exist_ok=True)
|
|
11895
|
+
pfile = os.path.join(pdir, "project-" + os.path.basename(filepath))
|
|
11896
|
+
if pfile != filepath:
|
|
11897
|
+
save_entries_to(pfile, entries, category)
|
|
11898
|
+
except Exception:
|
|
11899
|
+
pass
|
|
11900
|
+
return save_entries_to(filepath, entries, category)
|
|
11901
|
+
|
|
11902
|
+
def save_entries_to(filepath, entries, category):
|
|
11867
11903
|
existing = get_existing_hashes(filepath)
|
|
11868
11904
|
saved = 0
|
|
11869
11905
|
with open(filepath, 'a') as f:
|
|
@@ -19522,8 +19558,14 @@ start_dashboard() {
|
|
|
19522
19558
|
# Auto-open the dashboard in the browser, but ONLY for an interactive
|
|
19523
19559
|
# foreground session. loki_open_url (lib/browser-open.sh) refuses on
|
|
19524
19560
|
# no TTY, CI, test runners, LOKI_NO_BROWSER=1 or LOKI_NO_AUTO_OPEN=1.
|
|
19561
|
+
# CP-LEGACY (D57): open the Control Plane, not this legacy server,
|
|
19562
|
+
# unless LOKI_LEGACY_DASHBOARD=1 opts back in.
|
|
19525
19563
|
if [ "${BACKGROUND_MODE:-false}" != "true" ]; then
|
|
19526
|
-
|
|
19564
|
+
if [ "${LOKI_LEGACY_DASHBOARD:-0}" = "1" ]; then
|
|
19565
|
+
loki_open_url "${url_scheme}://127.0.0.1:$DASHBOARD_PORT/" 2>/dev/null || true
|
|
19566
|
+
elif [ -t 1 ] && [ "${LOKI_NO_AUTO_OPEN:-0}" != "1" ] && [ -x "${skill_dir}/autonomy/loki" ]; then
|
|
19567
|
+
LOKI_UI_NO_CLASSIC=1 "${skill_dir}/autonomy/loki" ui >/dev/null 2>&1 || true
|
|
19568
|
+
fi
|
|
19527
19569
|
fi
|
|
19528
19570
|
return 0
|
|
19529
19571
|
else
|
package/completions/_loki
CHANGED
|
@@ -188,6 +188,8 @@ function _loki_commands {
|
|
|
188
188
|
'deploy:Advisory deploy command (print-only)'
|
|
189
189
|
'docker:Run Loki in a Docker container'
|
|
190
190
|
'mcp:MCP server commands'
|
|
191
|
+
'merge:Green-only serial merge queue'
|
|
192
|
+
'acp:Agent Client Protocol agent over stdio'
|
|
191
193
|
'magic:Spec-driven component generation'
|
|
192
194
|
'assets:Export/import shareable team assets'
|
|
193
195
|
'spec:Living spec drift detection'
|
package/completions/loki.bash
CHANGED
|
@@ -5,7 +5,7 @@ _loki_completion() {
|
|
|
5
5
|
_init_completion || return
|
|
6
6
|
|
|
7
7
|
# Main subcommands (must match autonomy/loki main case statement)
|
|
8
|
-
local main_commands="start backlog quick monitor demo tour welcome init stop pause resume steer status next ship dashboard web serve api sandbox notify import github issue intent config provider reset memory compound checkpoint council dogfood projects enterprise secrets cockpit secure own handoff doctor watchdog audit metrics syslog onboard share proof receipt outcomes explain plan report cost estimate kpis stats test ci watch telemetry agent context ctx code run export review optimize heal modernize migrate cluster worktree wt trigger failover remote deploy docker mcp magic assets analyze compliance crash control workspace open otel preview quickstart rc rollback self-update sentrux setup-skill spec state template trust trust-metrics ultracode update verify voice why wiki bench cleanup logs grill docs cp version completions help keys answer"
|
|
8
|
+
local main_commands="start backlog quick monitor demo tour welcome init stop pause resume steer status next ship dashboard web serve api sandbox notify import github issue intent config provider reset memory compound checkpoint council dogfood projects enterprise secrets cockpit secure own handoff doctor watchdog audit metrics syslog onboard share proof receipt outcomes explain plan report cost estimate kpis stats test ci watch telemetry agent context ctx code run export review optimize heal modernize migrate cluster worktree wt trigger failover remote deploy docker mcp magic assets analyze compliance crash control workspace open otel preview quickstart rc rollback merge self-update sentrux setup-skill spec state template trust trust-metrics ultracode update verify voice why wiki bench cleanup logs grill docs cp version completions help keys answer acp"
|
|
9
9
|
|
|
10
10
|
# 1. If we are on the first argument (subcommand)
|
|
11
11
|
if [[ $cword -eq 1 ]]; then
|
package/dashboard/__init__.py
CHANGED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""REST API for runs: /api/v1/runs.
|
|
2
|
+
|
|
3
|
+
A thin, stable surface over code that already exists:
|
|
4
|
+
GET /api/v1/runs -> api_runs.list_runs (envelope kept intact)
|
|
5
|
+
GET /api/v1/runs/{id} -> api_runs.get_run (404 when the run is unknown)
|
|
6
|
+
POST /api/v1/runs -> the existing /api/control/start handler
|
|
7
|
+
POST /api/v1/runs/{id}/stop -> the existing /api/control/stop handler,
|
|
8
|
+
only when {id} is the current run
|
|
9
|
+
|
|
10
|
+
Auth is the dashboard's own scope check: "read" for GETs, "control" for POSTs.
|
|
11
|
+
The dashboard binds loopback by default; nothing here opens a listener.
|
|
12
|
+
"""
|
|
13
|
+
import os
|
|
14
|
+
from typing import Optional
|
|
15
|
+
|
|
16
|
+
from fastapi import APIRouter, Depends, HTTPException, Request
|
|
17
|
+
from pydantic import BaseModel, Field
|
|
18
|
+
|
|
19
|
+
from . import auth
|
|
20
|
+
|
|
21
|
+
router = APIRouter(prefix="/api/v1/runs", tags=["runs-v1"])
|
|
22
|
+
_READ = [Depends(auth.require_scope("read"))]
|
|
23
|
+
_CONTROL = [Depends(auth.require_scope("control"))]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _loki_dir() -> str:
|
|
27
|
+
try:
|
|
28
|
+
from . import server
|
|
29
|
+
return str(server._get_loki_dir())
|
|
30
|
+
except Exception:
|
|
31
|
+
return os.environ.get("LOKI_DIR") or os.path.join(os.getcwd(), ".loki")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class RunStartIn(BaseModel):
|
|
35
|
+
prd_text: Optional[str] = Field(default=None, max_length=1_048_576)
|
|
36
|
+
prd_path: Optional[str] = None
|
|
37
|
+
provider: str = "claude"
|
|
38
|
+
parallel: bool = False
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
async def _start_impl(request: Request, body: RunStartIn):
|
|
42
|
+
from . import server
|
|
43
|
+
return await server.start_build(request, server.StartBuildRequest(**body.model_dump()))
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
async def _stop_impl(request: Request):
|
|
47
|
+
from . import server
|
|
48
|
+
return await server.stop_session(request)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@router.get("", dependencies=_READ)
|
|
52
|
+
def list_runs_v1():
|
|
53
|
+
from . import api_runs
|
|
54
|
+
return api_runs.list_runs(_loki_dir())
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@router.get("/{run_id}", dependencies=_READ)
|
|
58
|
+
def get_run_v1(run_id: str):
|
|
59
|
+
from . import api_runs
|
|
60
|
+
env = api_runs.get_run(_loki_dir(), run_id)
|
|
61
|
+
if env.get("run") is None:
|
|
62
|
+
raise HTTPException(status_code=404, detail=env.get("reason") or "run not found")
|
|
63
|
+
return env
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@router.post("", dependencies=_CONTROL)
|
|
67
|
+
async def start_run_v1(request: Request, body: RunStartIn):
|
|
68
|
+
return await _start_impl(request, body)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@router.post("/{run_id}/stop", dependencies=_CONTROL)
|
|
72
|
+
async def stop_run_v1(run_id: str, request: Request):
|
|
73
|
+
from . import api_runs
|
|
74
|
+
env = api_runs.get_run(_loki_dir(), run_id)
|
|
75
|
+
row = env.get("run")
|
|
76
|
+
if row is None:
|
|
77
|
+
raise HTTPException(status_code=404, detail=env.get("reason") or "run not found")
|
|
78
|
+
if not row.get("current"):
|
|
79
|
+
raise HTTPException(status_code=409, detail="only the current run can be stopped")
|
|
80
|
+
return await _stop_impl(request)
|
package/dashboard/server.py
CHANGED
|
@@ -1362,6 +1362,8 @@ else:
|
|
|
1362
1362
|
# D51 Phase A: first-run onboarding + backlog API and its /start page.
|
|
1363
1363
|
from .api_start import router as api_start_router, START_HTML as _START_HTML
|
|
1364
1364
|
app.include_router(api_start_router)
|
|
1365
|
+
from .api_runs_v1 import router as api_runs_v1_router
|
|
1366
|
+
app.include_router(api_runs_v1_router)
|
|
1365
1367
|
|
|
1366
1368
|
|
|
1367
1369
|
@app.get("/start", include_in_schema=False, dependencies=[Depends(auth.require_scope("read"))])
|
package/docs/INSTALLATION.md
CHANGED
|
@@ -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:** v10.
|
|
5
|
+
**Version:** v10.7.1
|
|
6
6
|
|
|
7
7
|
**Engine note:** `loki "<task>"`, `loki owner/repo#N` and `loki quick` run the Loki 10 engine. `loki start` still routes to the legacy engine, which is being removed (planned work resumes 2026-10-07); prefer `loki owner/repo#N`. See [docs/v10/GUIDE.md](v10/GUIDE.md).
|
|
8
8
|
|
|
@@ -434,11 +434,15 @@ runs any loki command inside the published `asklokesh/loki-mode` image with zero
|
|
|
434
434
|
config:
|
|
435
435
|
|
|
436
436
|
```bash
|
|
437
|
+
loki docker start owner/repo#17 # work a GitHub issue or PR in Docker (primary path)
|
|
437
438
|
loki docker start prd.md # full local experience in Docker
|
|
438
439
|
loki docker status # any loki command works
|
|
439
440
|
loki docker --dry-run start prd.md # print the docker command, do not run
|
|
440
441
|
```
|
|
441
442
|
|
|
443
|
+
On macOS, pass your GitHub login for PRs with
|
|
444
|
+
`GH_TOKEN=$(gh auth token) loki docker start owner/repo#17`.
|
|
445
|
+
|
|
442
446
|
It bind-mounts the current folder to `/workspace`, so `.loki/` state (memory,
|
|
443
447
|
session, queue, checkpoints) persists on the host and resume and continuity
|
|
444
448
|
behave exactly like the local `loki` CLI. Auth is auto-detected:
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Loki in VS Code and JetBrains via ACP
|
|
2
|
+
|
|
3
|
+
`loki acp` speaks the Agent Client Protocol (newline-delimited JSON-RPC over
|
|
4
|
+
stdio). It supports `initialize`, `session/new`, `session/prompt` and
|
|
5
|
+
`session/cancel`. Each prompt runs `loki quick "<prompt>"` in the session's
|
|
6
|
+
working directory and streams the output back. A non-zero exit is reported as
|
|
7
|
+
"NOT VERIFIED"; nothing is claimed that did not run. Loki never receives editor
|
|
8
|
+
credentials, and no network listener is opened.
|
|
9
|
+
|
|
10
|
+
## JetBrains (AI Assistant with ACP support)
|
|
11
|
+
|
|
12
|
+
Edit `~/.jetbrains/acp.json`:
|
|
13
|
+
|
|
14
|
+
{"agent_servers": {"Loki": {"command": "loki", "args": ["acp"]}}}
|
|
15
|
+
|
|
16
|
+
## Zed
|
|
17
|
+
|
|
18
|
+
In settings.json:
|
|
19
|
+
|
|
20
|
+
{"agent_servers": {"Loki": {"command": "loki", "args": ["acp"]}}}
|
|
21
|
+
|
|
22
|
+
## VS Code
|
|
23
|
+
|
|
24
|
+
Use any ACP client extension and register a custom agent with command `loki`
|
|
25
|
+
and args `acp`.
|
|
26
|
+
|
|
27
|
+
## Smoke test
|
|
28
|
+
|
|
29
|
+
printf '%s\n' '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":1}}' | loki acp
|