@misterhuydo/sentinel 1.0.85 → 1.0.87
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/.hint-lock
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2026-03-23T05:
|
|
1
|
+
2026-03-23T05:33:10.957Z
|
package/.cairn/session.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"message": "Auto-checkpoint at 2026-03-23T05:
|
|
3
|
-
"checkpoint_at": "2026-03-23T05:
|
|
2
|
+
"message": "Auto-checkpoint at 2026-03-23T05:55:20.486Z",
|
|
3
|
+
"checkpoint_at": "2026-03-23T05:55:20.487Z",
|
|
4
4
|
"active_files": [],
|
|
5
5
|
"notes": [],
|
|
6
6
|
"mtime_snapshot": {}
|
package/package.json
CHANGED
|
@@ -113,6 +113,10 @@ What you can do (tools available):
|
|
|
113
113
|
21. tail_log — Fetch the last N lines of a log source live, without a grep filter.
|
|
114
114
|
e.g. "show recent SSOLWA logs", "tail STS", "last 200 lines from 1881 logs"
|
|
115
115
|
|
|
116
|
+
22. post_file — Upload a text file to the Slack conversation (diff, log excerpt, report, CSV).
|
|
117
|
+
Use when output is too large for chat, or the user asks to download/export something.
|
|
118
|
+
e.g. "give me that as a file", "export the log", "send me the diff"
|
|
119
|
+
|
|
116
120
|
When someone asks what you can do, what you support, what your capabilities are, or how you can help,
|
|
117
121
|
reply with a short summary grouped by category:
|
|
118
122
|
|
|
@@ -154,6 +158,9 @@ reply with a short summary grouped by category:
|
|
|
154
158
|
*Self-management*
|
|
155
159
|
• `upgrade_sentinel` — git pull + pip install + restart — "upgrade sentinel", "update yourself"
|
|
156
160
|
|
|
161
|
+
*File sharing*
|
|
162
|
+
• `post_file` — upload a file to Slack — "give me that as a file", "export the log", "send me the diff"
|
|
163
|
+
|
|
157
164
|
Tone: direct, professional, like a senior engineer who owns the system.
|
|
158
165
|
Don't pad responses. Don't say "Great question!" or "Certainly!".
|
|
159
166
|
If you don't know something, use a tool to find out before saying you don't know.
|
|
@@ -551,6 +558,34 @@ _TOOLS = [
|
|
|
551
558
|
"required": ["source"],
|
|
552
559
|
},
|
|
553
560
|
},
|
|
561
|
+
{
|
|
562
|
+
"name": "post_file",
|
|
563
|
+
"description": (
|
|
564
|
+
"Upload a text file directly to the Slack conversation so the user can read or download it. "
|
|
565
|
+
"Use when: output is too large for a chat message, the user asks to 'download', 'export', or "
|
|
566
|
+
"'send as a file', or when formatted content (diffs, logs, CSVs, reports) is clearer as a file. "
|
|
567
|
+
"e.g. 'give me that as a file', 'export the log', 'send me the diff for PR #41', "
|
|
568
|
+
"'download the health report', 'export recent errors as CSV'"
|
|
569
|
+
),
|
|
570
|
+
"input_schema": {
|
|
571
|
+
"type": "object",
|
|
572
|
+
"properties": {
|
|
573
|
+
"content": {
|
|
574
|
+
"type": "string",
|
|
575
|
+
"description": "The full text content of the file to upload",
|
|
576
|
+
},
|
|
577
|
+
"filename": {
|
|
578
|
+
"type": "string",
|
|
579
|
+
"description": "Filename with extension, e.g. 'fix-ab12.diff', 'sentinel-report.txt', 'errors.csv', 'ssolwa.log'",
|
|
580
|
+
},
|
|
581
|
+
"title": {
|
|
582
|
+
"type": "string",
|
|
583
|
+
"description": "Optional display title shown above the file in Slack (defaults to filename)",
|
|
584
|
+
},
|
|
585
|
+
},
|
|
586
|
+
"required": ["content", "filename"],
|
|
587
|
+
},
|
|
588
|
+
},
|
|
554
589
|
]
|
|
555
590
|
|
|
556
591
|
|
|
@@ -617,7 +652,7 @@ def _git_pull(path: Path) -> dict:
|
|
|
617
652
|
|
|
618
653
|
# ── Tool execution ────────────────────────────────────────────────────────────
|
|
619
654
|
|
|
620
|
-
async def _run_tool(name: str, inputs: dict, cfg_loader, store, slack_client=None, user_id: str = "") -> str:
|
|
655
|
+
async def _run_tool(name: str, inputs: dict, cfg_loader, store, slack_client=None, user_id: str = "", channel: str = "") -> str:
|
|
621
656
|
if name == "get_status":
|
|
622
657
|
hours = int(inputs.get("hours", 24))
|
|
623
658
|
errors = store.get_recent_errors(hours)
|
|
@@ -1206,6 +1241,27 @@ async def _run_tool(name: str, inputs: dict, cfg_loader, store, slack_client=Non
|
|
|
1206
1241
|
results.append({"source": props.stem, "error": str(e)})
|
|
1207
1242
|
return json.dumps({"results": results})
|
|
1208
1243
|
|
|
1244
|
+
if name == "post_file":
|
|
1245
|
+
if not slack_client or not channel:
|
|
1246
|
+
return json.dumps({"error": "No Slack channel context — cannot upload file"})
|
|
1247
|
+
content = inputs.get("content", "")
|
|
1248
|
+
filename = inputs.get("filename", "sentinel-output.txt")
|
|
1249
|
+
title = inputs.get("title", filename)
|
|
1250
|
+
if not content:
|
|
1251
|
+
return json.dumps({"error": "No content provided"})
|
|
1252
|
+
try:
|
|
1253
|
+
await slack_client.files_upload_v2(
|
|
1254
|
+
channel=channel,
|
|
1255
|
+
content=content,
|
|
1256
|
+
filename=filename,
|
|
1257
|
+
title=title,
|
|
1258
|
+
)
|
|
1259
|
+
logger.info("Boss post_file: uploaded %s (%d bytes) to %s", filename, len(content), channel)
|
|
1260
|
+
return json.dumps({"ok": True, "filename": filename, "bytes": len(content)})
|
|
1261
|
+
except Exception as e:
|
|
1262
|
+
logger.warning("Boss post_file failed: %s", e)
|
|
1263
|
+
return json.dumps({"error": str(e)})
|
|
1264
|
+
|
|
1209
1265
|
if name == "my_stats":
|
|
1210
1266
|
hours = int(inputs.get("hours", 168))
|
|
1211
1267
|
errors = store.get_recent_errors(hours)
|
|
@@ -1464,6 +1520,7 @@ async def _handle_with_api(
|
|
|
1464
1520
|
user_name: str = "",
|
|
1465
1521
|
user_id: str = "",
|
|
1466
1522
|
attachments: list | None = None,
|
|
1523
|
+
channel: str = "",
|
|
1467
1524
|
) -> tuple[str, bool]:
|
|
1468
1525
|
import anthropic
|
|
1469
1526
|
|
|
@@ -1524,7 +1581,7 @@ async def _handle_with_api(
|
|
|
1524
1581
|
messages.append({"role": "assistant", "content": response.content})
|
|
1525
1582
|
tool_results = []
|
|
1526
1583
|
for tc in tool_blocks:
|
|
1527
|
-
result = await _run_tool(tc.name, tc.input, cfg_loader, store, slack_client=slack_client, user_id=user_id)
|
|
1584
|
+
result = await _run_tool(tc.name, tc.input, cfg_loader, store, slack_client=slack_client, user_id=user_id, channel=channel)
|
|
1528
1585
|
logger.info("Boss tool: %s(%s) → %s", tc.name, tc.input, result[:120])
|
|
1529
1586
|
tool_results.append({
|
|
1530
1587
|
"type": "tool_result",
|
|
@@ -1545,6 +1602,7 @@ async def handle_message(
|
|
|
1545
1602
|
user_name: str = "",
|
|
1546
1603
|
user_id: str = "",
|
|
1547
1604
|
attachments: list | None = None,
|
|
1605
|
+
channel: str = "",
|
|
1548
1606
|
) -> tuple[str, bool]:
|
|
1549
1607
|
"""
|
|
1550
1608
|
Process one user message through the Sentinel Boss (Claude with tool use).
|
|
@@ -1566,7 +1624,7 @@ async def handle_message(
|
|
|
1566
1624
|
import anthropic # noqa: F401
|
|
1567
1625
|
return await _handle_with_api(
|
|
1568
1626
|
message, history, cfg_loader, store, slack_client=slack_client,
|
|
1569
|
-
user_name=user_name, user_id=user_id, attachments=attachments,
|
|
1627
|
+
user_name=user_name, user_id=user_id, attachments=attachments, channel=channel,
|
|
1570
1628
|
)
|
|
1571
1629
|
except Exception as api_err:
|
|
1572
1630
|
err_str = str(api_err)
|
|
@@ -389,6 +389,7 @@ async def _run_turn(session: _Session, message: str, client, cfg_loader, store,
|
|
|
389
389
|
user_name=session.user_name,
|
|
390
390
|
user_id=session.user_id,
|
|
391
391
|
attachments=attachments or [],
|
|
392
|
+
channel=channel,
|
|
392
393
|
)
|
|
393
394
|
except Exception as e:
|
|
394
395
|
logger.exception("Sentinel Boss error: %s", e)
|
|
@@ -391,7 +391,7 @@ class StateStore:
|
|
|
391
391
|
).fetchall()
|
|
392
392
|
return [dict(r) for r in rows]
|
|
393
393
|
|
|
394
|
-
|
|
394
|
+
def save_conversation(self, user_id: str, history: list):
|
|
395
395
|
"""Persist the last N messages of a user conversation to SQLite."""
|
|
396
396
|
trimmed = history[-self._MAX_HISTORY_MESSAGES:]
|
|
397
397
|
with self._conn() as conn:
|