loki-mode 5.34.0 → 5.35.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/README.md +5 -5
- package/SKILL.md +4 -4
- package/VERSION +1 -1
- package/api/README.md +1 -1
- package/api/server.js +1 -1
- package/api/server.ts +5 -5
- package/api/test.js +1 -1
- package/autonomy/api-server.js +6 -4
- package/autonomy/completion-council.sh +4 -2
- package/autonomy/hooks/store-episode.sh +2 -2
- package/autonomy/loki +84 -54
- package/autonomy/run.sh +579 -35
- package/autonomy/sandbox.sh +3 -9
- package/autonomy/serve.sh +10 -10
- package/dashboard/__init__.py +1 -1
- package/dashboard/server.py +16 -16
- package/dashboard/static/index.html +359 -58
- package/docs/INSTALLATION.md +4 -4
- package/docs/SYNERGY-ROADMAP.md +1 -1
- package/docs/architecture/DASHBOARD_V2_ARCHITECTURE.md +4 -3
- package/docs/dashboard-guide.md +1 -1
- package/memory/layers/index_layer.py +4 -4
- package/memory/layers/timeline_layer.py +5 -5
- package/memory/retrieval.py +10 -2
- package/memory/storage.py +1 -1
- package/memory/token_economics.py +12 -8
- package/package.json +1 -1
package/autonomy/sandbox.sh
CHANGED
|
@@ -56,7 +56,7 @@ SANDBOX_MEMORY="${LOKI_SANDBOX_MEMORY:-4g}"
|
|
|
56
56
|
SANDBOX_READONLY="${LOKI_SANDBOX_READONLY:-false}"
|
|
57
57
|
|
|
58
58
|
# API ports
|
|
59
|
-
API_PORT="${
|
|
59
|
+
API_PORT="${LOKI_DASHBOARD_PORT:-57374}"
|
|
60
60
|
DASHBOARD_PORT="${LOKI_DASHBOARD_PORT:-57374}"
|
|
61
61
|
|
|
62
62
|
# Security: Prompt injection disabled by default for enterprise security
|
|
@@ -766,12 +766,7 @@ start_sandbox() {
|
|
|
766
766
|
validate_project_dir || return 1
|
|
767
767
|
ensure_image
|
|
768
768
|
|
|
769
|
-
# Check port availability
|
|
770
|
-
if ! check_port_available "$API_PORT"; then
|
|
771
|
-
log_error "Port $API_PORT is already in use"
|
|
772
|
-
log_info "Set LOKI_API_PORT to use a different port"
|
|
773
|
-
return 1
|
|
774
|
-
fi
|
|
769
|
+
# Check port availability (unified dashboard/API port)
|
|
775
770
|
if ! check_port_available "$DASHBOARD_PORT"; then
|
|
776
771
|
log_error "Port $DASHBOARD_PORT is already in use"
|
|
777
772
|
log_info "Set LOKI_DASHBOARD_PORT to use a different port"
|
|
@@ -898,8 +893,7 @@ start_sandbox() {
|
|
|
898
893
|
|
|
899
894
|
# Expose ports
|
|
900
895
|
docker_args+=(
|
|
901
|
-
"--publish" "$API_PORT:
|
|
902
|
-
"--publish" "$DASHBOARD_PORT:57374"
|
|
896
|
+
"--publish" "$API_PORT:57374"
|
|
903
897
|
)
|
|
904
898
|
|
|
905
899
|
# Expose additional ports for testing (e.g., LOKI_EXTRA_PORTS="3000:3000,8080:8080")
|
package/autonomy/serve.sh
CHANGED
|
@@ -9,15 +9,15 @@
|
|
|
9
9
|
# loki serve [OPTIONS]
|
|
10
10
|
#
|
|
11
11
|
# Options:
|
|
12
|
-
# --port, -p <port> Port to listen on (default:
|
|
12
|
+
# --port, -p <port> Port to listen on (default: 57374)
|
|
13
13
|
# --host, -h <host> Host to bind to (default: localhost)
|
|
14
14
|
# --no-cors Disable CORS
|
|
15
15
|
# --no-auth Disable authentication
|
|
16
16
|
# --help Show help message
|
|
17
17
|
#
|
|
18
18
|
# Environment Variables:
|
|
19
|
-
#
|
|
20
|
-
#
|
|
19
|
+
# LOKI_DASHBOARD_PORT Port (default: 57374)
|
|
20
|
+
# LOKI_DASHBOARD_HOST Host (default: localhost)
|
|
21
21
|
# LOKI_API_TOKEN API token for remote access
|
|
22
22
|
#===============================================================================
|
|
23
23
|
|
|
@@ -28,8 +28,8 @@ PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
|
28
28
|
API_DIR="$PROJECT_DIR/api"
|
|
29
29
|
|
|
30
30
|
# Default configuration
|
|
31
|
-
PORT="${
|
|
32
|
-
HOST="${
|
|
31
|
+
PORT="${LOKI_DASHBOARD_PORT:-57374}"
|
|
32
|
+
HOST="${LOKI_DASHBOARD_HOST:-localhost}"
|
|
33
33
|
CORS="true"
|
|
34
34
|
AUTH="true"
|
|
35
35
|
|
|
@@ -63,7 +63,7 @@ Usage:
|
|
|
63
63
|
loki serve [OPTIONS]
|
|
64
64
|
|
|
65
65
|
Options:
|
|
66
|
-
--port, -p <port> Port to listen on (default:
|
|
66
|
+
--port, -p <port> Port to listen on (default: 57374)
|
|
67
67
|
--host <host> Host to bind to (default: localhost)
|
|
68
68
|
--no-cors Disable CORS
|
|
69
69
|
--no-auth Disable authentication
|
|
@@ -71,14 +71,14 @@ Options:
|
|
|
71
71
|
--help Show this help message
|
|
72
72
|
|
|
73
73
|
Environment Variables:
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
LOKI_DASHBOARD_PORT Port (overridden by --port)
|
|
75
|
+
LOKI_DASHBOARD_HOST Host (overridden by --host)
|
|
76
76
|
LOKI_API_TOKEN API token for remote access
|
|
77
77
|
LOKI_DIR Loki installation directory
|
|
78
78
|
LOKI_DEBUG Enable debug output
|
|
79
79
|
|
|
80
80
|
Examples:
|
|
81
|
-
# Start with defaults (localhost:
|
|
81
|
+
# Start with defaults (localhost:57374)
|
|
82
82
|
loki serve
|
|
83
83
|
|
|
84
84
|
# Custom port
|
|
@@ -89,7 +89,7 @@ Examples:
|
|
|
89
89
|
loki serve --host 0.0.0.0
|
|
90
90
|
|
|
91
91
|
# Connect from another machine
|
|
92
|
-
curl -H "Authorization: Bearer \$TOKEN" http://server:
|
|
92
|
+
curl -H "Authorization: Bearer \$TOKEN" http://server:57374/health
|
|
93
93
|
|
|
94
94
|
EOF
|
|
95
95
|
}
|
package/dashboard/__init__.py
CHANGED
package/dashboard/server.py
CHANGED
|
@@ -188,7 +188,7 @@ class ConnectionManager:
|
|
|
188
188
|
|
|
189
189
|
|
|
190
190
|
manager = ConnectionManager()
|
|
191
|
-
start_time = datetime.now()
|
|
191
|
+
start_time = datetime.now(timezone.utc)
|
|
192
192
|
|
|
193
193
|
|
|
194
194
|
@asynccontextmanager
|
|
@@ -217,8 +217,8 @@ app.add_middleware(
|
|
|
217
217
|
CORSMiddleware,
|
|
218
218
|
allow_origins=[o.strip() for o in _cors_origins if o.strip()],
|
|
219
219
|
allow_credentials=True,
|
|
220
|
-
allow_methods=["
|
|
221
|
-
allow_headers=["
|
|
220
|
+
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
|
|
221
|
+
allow_headers=["Content-Type", "Authorization", "X-Requested-With"],
|
|
222
222
|
)
|
|
223
223
|
|
|
224
224
|
# Static file serving is configured at the end of the file (after all API routes)
|
|
@@ -236,7 +236,7 @@ async def health_check() -> dict[str, str]:
|
|
|
236
236
|
async def get_status() -> StatusResponse:
|
|
237
237
|
"""Get system status from .loki/ session files."""
|
|
238
238
|
loki_dir = _get_loki_dir()
|
|
239
|
-
uptime = (datetime.now() - start_time).total_seconds()
|
|
239
|
+
uptime = (datetime.now(timezone.utc) - start_time).total_seconds()
|
|
240
240
|
|
|
241
241
|
# Read dashboard-state.json (written by run.sh every 2 seconds)
|
|
242
242
|
state_file = loki_dir / "dashboard-state.json"
|
|
@@ -681,7 +681,7 @@ async def update_task(
|
|
|
681
681
|
|
|
682
682
|
# Handle status change to completed
|
|
683
683
|
if "status" in update_data and update_data["status"] == TaskStatus.DONE:
|
|
684
|
-
update_data["completed_at"] = datetime.now()
|
|
684
|
+
update_data["completed_at"] = datetime.now(timezone.utc)
|
|
685
685
|
|
|
686
686
|
for field, value in update_data.items():
|
|
687
687
|
setattr(task, field, value)
|
|
@@ -748,7 +748,7 @@ async def move_task(
|
|
|
748
748
|
|
|
749
749
|
# Set completed_at if moving to completed
|
|
750
750
|
if move.status == TaskStatus.DONE and old_status != TaskStatus.DONE:
|
|
751
|
-
task.completed_at = datetime.now()
|
|
751
|
+
task.completed_at = datetime.now(timezone.utc)
|
|
752
752
|
elif move.status != TaskStatus.DONE:
|
|
753
753
|
task.completed_at = None
|
|
754
754
|
|
|
@@ -1135,10 +1135,10 @@ _SAFE_ID_RE = re.compile(r'^[a-zA-Z0-9_-]+$')
|
|
|
1135
1135
|
|
|
1136
1136
|
def _sanitize_agent_id(agent_id: str) -> str:
|
|
1137
1137
|
"""Validate agent_id contains only safe characters for file paths."""
|
|
1138
|
-
if not agent_id or ".." in agent_id or not _SAFE_ID_RE.match(agent_id):
|
|
1138
|
+
if not agent_id or len(agent_id) > 128 or ".." in agent_id or not _SAFE_ID_RE.match(agent_id):
|
|
1139
1139
|
raise HTTPException(
|
|
1140
1140
|
status_code=400,
|
|
1141
|
-
detail="Invalid agent_id: must
|
|
1141
|
+
detail="Invalid agent_id: must be 1-128 chars of alphanumeric, hyphens, and underscores",
|
|
1142
1142
|
)
|
|
1143
1143
|
return agent_id
|
|
1144
1144
|
|
|
@@ -1611,7 +1611,7 @@ async def pause_session():
|
|
|
1611
1611
|
"""Pause the current session by creating PAUSE file."""
|
|
1612
1612
|
pause_file = _get_loki_dir() / "PAUSE"
|
|
1613
1613
|
pause_file.parent.mkdir(parents=True, exist_ok=True)
|
|
1614
|
-
pause_file.write_text(datetime.now().isoformat())
|
|
1614
|
+
pause_file.write_text(datetime.now(timezone.utc).isoformat())
|
|
1615
1615
|
return {"success": True, "message": "Session paused"}
|
|
1616
1616
|
|
|
1617
1617
|
|
|
@@ -1632,7 +1632,7 @@ async def stop_session():
|
|
|
1632
1632
|
"""Stop the session by creating STOP file and sending SIGTERM."""
|
|
1633
1633
|
stop_file = _get_loki_dir() / "STOP"
|
|
1634
1634
|
stop_file.parent.mkdir(parents=True, exist_ok=True)
|
|
1635
|
-
stop_file.write_text(datetime.now().isoformat())
|
|
1635
|
+
stop_file.write_text(datetime.now(timezone.utc).isoformat())
|
|
1636
1636
|
|
|
1637
1637
|
# Try to kill the process
|
|
1638
1638
|
pid_file = _get_loki_dir() / "loki.pid"
|
|
@@ -1979,7 +1979,7 @@ async def force_council_review():
|
|
|
1979
1979
|
signal_dir = _get_loki_dir() / "signals"
|
|
1980
1980
|
signal_dir.mkdir(parents=True, exist_ok=True)
|
|
1981
1981
|
(signal_dir / "COUNCIL_REVIEW_REQUESTED").write_text(
|
|
1982
|
-
datetime.now().isoformat()
|
|
1982
|
+
datetime.now(timezone.utc).isoformat()
|
|
1983
1983
|
)
|
|
1984
1984
|
return {"success": True, "message": "Council review requested"}
|
|
1985
1985
|
|
|
@@ -1990,15 +1990,15 @@ async def force_council_review():
|
|
|
1990
1990
|
|
|
1991
1991
|
class CheckpointCreate(BaseModel):
|
|
1992
1992
|
"""Schema for creating a checkpoint."""
|
|
1993
|
-
message: Optional[str] = Field(None, description="Optional description for the checkpoint")
|
|
1993
|
+
message: Optional[str] = Field(None, max_length=500, description="Optional description for the checkpoint")
|
|
1994
1994
|
|
|
1995
1995
|
|
|
1996
1996
|
def _sanitize_checkpoint_id(checkpoint_id: str) -> str:
|
|
1997
1997
|
"""Validate checkpoint_id contains only safe characters for file paths."""
|
|
1998
|
-
if not checkpoint_id or ".." in checkpoint_id or not _SAFE_ID_RE.match(checkpoint_id):
|
|
1998
|
+
if not checkpoint_id or len(checkpoint_id) > 128 or ".." in checkpoint_id or not _SAFE_ID_RE.match(checkpoint_id):
|
|
1999
1999
|
raise HTTPException(
|
|
2000
2000
|
status_code=400,
|
|
2001
|
-
detail="Invalid checkpoint_id: must
|
|
2001
|
+
detail="Invalid checkpoint_id: must be 1-128 chars of alphanumeric, hyphens, and underscores",
|
|
2002
2002
|
)
|
|
2003
2003
|
return checkpoint_id
|
|
2004
2004
|
|
|
@@ -2241,7 +2241,7 @@ async def pause_agent(agent_id: str):
|
|
|
2241
2241
|
signal_dir = _get_loki_dir() / "signals"
|
|
2242
2242
|
signal_dir.mkdir(parents=True, exist_ok=True)
|
|
2243
2243
|
(signal_dir / f"PAUSE_AGENT_{agent_id}").write_text(
|
|
2244
|
-
datetime.now().isoformat()
|
|
2244
|
+
datetime.now(timezone.utc).isoformat()
|
|
2245
2245
|
)
|
|
2246
2246
|
return {"success": True, "message": f"Pause signal sent to agent {agent_id}"}
|
|
2247
2247
|
|
|
@@ -2290,7 +2290,7 @@ async def get_logs(lines: int = 100):
|
|
|
2290
2290
|
for log_file in log_files[:1]:
|
|
2291
2291
|
try:
|
|
2292
2292
|
# Use file mtime as fallback timestamp
|
|
2293
|
-
file_mtime = datetime.fromtimestamp(log_file.stat().st_mtime).strftime(
|
|
2293
|
+
file_mtime = datetime.fromtimestamp(log_file.stat().st_mtime, tz=timezone.utc).strftime(
|
|
2294
2294
|
"%Y-%m-%dT%H:%M:%S"
|
|
2295
2295
|
)
|
|
2296
2296
|
content = log_file.read_text()
|