@misterhuydo/sentinel 1.1.6 → 1.1.8
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/.cairn/session.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"message": "Auto-checkpoint at 2026-03-
|
|
3
|
-
"checkpoint_at": "2026-03-
|
|
2
|
+
"message": "Auto-checkpoint at 2026-03-23T09:16:11.394Z",
|
|
3
|
+
"checkpoint_at": "2026-03-23T09:16:11.395Z",
|
|
4
4
|
"active_files": [],
|
|
5
5
|
"notes": [],
|
|
6
6
|
"mtime_snapshot": {}
|
package/package.json
CHANGED
|
@@ -181,6 +181,17 @@ When to act vs. when to ask:
|
|
|
181
181
|
- If a tool call will take a moment (search, fetch, pull), prefix your reply with a brief "working" line ending in "..." before the results, e.g. "Searching SSOLWA for TryDig activity..." then the actual output.
|
|
182
182
|
Never just say a working line and stop — always follow it with the results in the same message.
|
|
183
183
|
|
|
184
|
+
Session context — critical rules:
|
|
185
|
+
- Loaded conversation history is prior-session background only. It may be hours or days old.
|
|
186
|
+
- NEVER say "the previous search", "I already fetched", "as I found earlier", or any phrase implying you already did part of the current task — unless a tool result appears in THIS response's tool calls.
|
|
187
|
+
- When handling a new request, call the tools fresh. Do not assume any prior tool result is still current or that any prior step "counts" toward the current task.
|
|
188
|
+
- The only exception: if the user explicitly asks about something from the history ("what did you find earlier?"), you may reference it — but note it is from a prior session.
|
|
189
|
+
|
|
190
|
+
Avoid redundant tool calls:
|
|
191
|
+
- If a broad search (e.g. search_logs with no source filter) already returned results for a query, do NOT repeat the same search with a source filter to "refine" — use the results you already have.
|
|
192
|
+
- If a tool call fails (wrong source name, config not found), do NOT retry the entire search from scratch. Continue with what succeeded, and note the failure in your reply.
|
|
193
|
+
- One pass per task: gather all needed data in a single round of tool calls, then produce the final answer.
|
|
194
|
+
|
|
184
195
|
Issue identification — before calling create_issue:
|
|
185
196
|
1. Determine if the message is a REAL issue/task (bug report, feature request, investigation ask)
|
|
186
197
|
vs. a status question, tool query, or casual chat. If not an issue, just answer normally.
|
|
@@ -773,6 +784,48 @@ def _git_pull(path: Path) -> dict:
|
|
|
773
784
|
return {"status": "error", "detail": str(e)}
|
|
774
785
|
|
|
775
786
|
|
|
787
|
+
# ── Log-source name resolver ──────────────────────────────────────────────────
|
|
788
|
+
|
|
789
|
+
def _filter_log_sources(props_files: list, source_hint: str) -> list:
|
|
790
|
+
"""
|
|
791
|
+
Return the subset of props_files whose log source matches source_hint.
|
|
792
|
+
|
|
793
|
+
Matching is tried in order (first match wins per file):
|
|
794
|
+
1. Substring of the filename stem (e.g. "sts" → STS.properties)
|
|
795
|
+
2. Substring of REMOTE_SERVICE_USER (e.g. "ssolwa" → ...SSOLoginWebApp...)
|
|
796
|
+
3. Substring of HOSTS (e.g. hostname fragment)
|
|
797
|
+
|
|
798
|
+
Case-insensitive throughout. An empty source_hint returns all files unchanged.
|
|
799
|
+
"""
|
|
800
|
+
if not source_hint:
|
|
801
|
+
return props_files
|
|
802
|
+
hint = source_hint.lower()
|
|
803
|
+
|
|
804
|
+
def _props_contains(path: Path, key: str, hint: str) -> bool:
|
|
805
|
+
try:
|
|
806
|
+
for line in path.read_text(encoding="utf-8", errors="replace").splitlines():
|
|
807
|
+
stripped = line.strip()
|
|
808
|
+
if stripped.startswith("#"):
|
|
809
|
+
continue
|
|
810
|
+
if stripped.upper().startswith(key + "="):
|
|
811
|
+
val = stripped.split("=", 1)[1].partition("#")[0].strip().lower()
|
|
812
|
+
if hint in val:
|
|
813
|
+
return True
|
|
814
|
+
except OSError:
|
|
815
|
+
pass
|
|
816
|
+
return False
|
|
817
|
+
|
|
818
|
+
matched = []
|
|
819
|
+
for p in props_files:
|
|
820
|
+
if hint in p.stem.lower():
|
|
821
|
+
matched.append(p)
|
|
822
|
+
elif _props_contains(p, "REMOTE_SERVICE_USER", hint):
|
|
823
|
+
matched.append(p)
|
|
824
|
+
elif _props_contains(p, "HOSTS", hint):
|
|
825
|
+
matched.append(p)
|
|
826
|
+
return matched
|
|
827
|
+
|
|
828
|
+
|
|
776
829
|
# ── Tool execution ────────────────────────────────────────────────────────────
|
|
777
830
|
|
|
778
831
|
async def _run_tool(name: str, inputs: dict, cfg_loader, store, slack_client=None, user_id: str = "", channel: str = "", is_admin: bool = False) -> str:
|
|
@@ -1010,9 +1063,7 @@ async def _run_tool(name: str, inputs: dict, cfg_loader, store, slack_client=Non
|
|
|
1010
1063
|
script = Path(__file__).resolve().parent.parent / "scripts" / "fetch_log.sh"
|
|
1011
1064
|
log_cfg_dir = Path("config") / "log-configs"
|
|
1012
1065
|
if script.exists() and log_cfg_dir.exists():
|
|
1013
|
-
props_files = sorted(log_cfg_dir.glob("*.properties"))
|
|
1014
|
-
if source:
|
|
1015
|
-
props_files = [p for p in props_files if source in p.stem.lower()]
|
|
1066
|
+
props_files = _filter_log_sources(sorted(log_cfg_dir.glob("*.properties")), source)
|
|
1016
1067
|
if props_files:
|
|
1017
1068
|
live_results = []
|
|
1018
1069
|
for props in props_files:
|
|
@@ -1159,9 +1210,7 @@ async def _run_tool(name: str, inputs: dict, cfg_loader, store, slack_client=Non
|
|
|
1159
1210
|
if not log_cfg_dir.exists():
|
|
1160
1211
|
return json.dumps({"error": "config/log-configs/ not found"})
|
|
1161
1212
|
|
|
1162
|
-
props_files = sorted(log_cfg_dir.glob("*.properties"))
|
|
1163
|
-
if source_filter:
|
|
1164
|
-
props_files = [p for p in props_files if source_filter in p.stem.lower()]
|
|
1213
|
+
props_files = _filter_log_sources(sorted(log_cfg_dir.glob("*.properties")), source_filter)
|
|
1165
1214
|
if not props_files:
|
|
1166
1215
|
return json.dumps({"error": f"No log-config found matching '{source_filter}'"})
|
|
1167
1216
|
|
|
@@ -371,12 +371,32 @@ async def _run_turn(session: _Session, message: str, client, cfg_loader, store,
|
|
|
371
371
|
channel = session.channel
|
|
372
372
|
|
|
373
373
|
# Load persisted history from DB on the first turn of a new session.
|
|
374
|
-
#
|
|
374
|
+
# - _clean_history strips orphaned tool_use turns from a previous crashed session.
|
|
375
|
+
# - Trim to last 6 exchanges (12 messages) to prevent stale tool results from bleeding
|
|
376
|
+
# into the current session and causing the model to treat old work as already done.
|
|
377
|
+
# - Inject a session boundary marker so the model clearly sees where prior context ends.
|
|
375
378
|
if not session.history_loaded:
|
|
376
|
-
|
|
379
|
+
loaded = _clean_history(store.load_conversation(session.user_id))
|
|
380
|
+
if loaded:
|
|
381
|
+
# Keep only the most recent 6 exchanges from prior session
|
|
382
|
+
_PRIOR_TURNS = 6
|
|
383
|
+
trimmed = loaded[-(_PRIOR_TURNS * 2):]
|
|
384
|
+
# Prepend a boundary pair so the model treats everything before it as old context
|
|
385
|
+
session.history = [
|
|
386
|
+
{
|
|
387
|
+
"role": "user",
|
|
388
|
+
"content": "[system: new session started — the following is prior conversation context only]",
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
"role": "assistant",
|
|
392
|
+
"content": [{"type": "text", "text": "Understood. I'll treat the prior context as reference only and handle your new request fresh."}],
|
|
393
|
+
},
|
|
394
|
+
] + trimmed
|
|
395
|
+
else:
|
|
396
|
+
session.history = []
|
|
377
397
|
session.history_loaded = True
|
|
378
398
|
|
|
379
|
-
# Trim history to avoid context overflow on long conversations
|
|
399
|
+
# Trim history to avoid context overflow on long active conversations
|
|
380
400
|
if len(session.history) > _MAX_HISTORY_TURNS * 2:
|
|
381
401
|
session.history = session.history[-(_MAX_HISTORY_TURNS * 2):]
|
|
382
402
|
|