arkaos 2.48.0 → 2.49.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/VERSION +1 -1
- package/config/hooks/stop.sh +34 -0
- package/config/hooks/user-prompt-submit.sh +20 -0
- package/core/governance/__pycache__/compliance_telemetry.cpython-313.pyc +0 -0
- package/core/governance/__pycache__/meta_tag_check.cpython-313.pyc +0 -0
- package/core/governance/meta_tag_check.py +63 -0
- package/package.json +1 -1
- package/pyproject.toml +1 -1
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.
|
|
1
|
+
2.49.0
|
package/config/hooks/stop.sh
CHANGED
|
@@ -162,6 +162,38 @@ try:
|
|
|
162
162
|
except Exception:
|
|
163
163
|
pass
|
|
164
164
|
|
|
165
|
+
# PR30 v2.49.0 — Meta-tag soft block. Mirrors the KB cite-check
|
|
166
|
+
# pipeline. Records whether the closing message carried the required
|
|
167
|
+
# [arka:meta] one-liner; persists result to /tmp/arkaos-meta/<session>.json
|
|
168
|
+
# so the next UserPromptSubmit can surface a nudge if missing.
|
|
169
|
+
meta_passed = True
|
|
170
|
+
meta_reason = "trivial"
|
|
171
|
+
meta_suggestion: str | None = None
|
|
172
|
+
try:
|
|
173
|
+
from core.governance.meta_tag_check import check_meta_tag
|
|
174
|
+
mr = check_meta_tag(last)
|
|
175
|
+
meta_passed = mr.passed
|
|
176
|
+
meta_reason = mr.reason
|
|
177
|
+
meta_suggestion = mr.suggestion
|
|
178
|
+
if safe_sid:
|
|
179
|
+
prev_umask = os.umask(0o077)
|
|
180
|
+
try:
|
|
181
|
+
meta_dir = Path("/tmp/arkaos-meta")
|
|
182
|
+
meta_dir.mkdir(parents=True, exist_ok=True)
|
|
183
|
+
meta_path = meta_dir / f"{safe_sid}.json"
|
|
184
|
+
meta_path.write_text(
|
|
185
|
+
json.dumps({
|
|
186
|
+
"passed": mr.passed,
|
|
187
|
+
"reason": mr.reason,
|
|
188
|
+
"suggestion": mr.suggestion,
|
|
189
|
+
}),
|
|
190
|
+
encoding="utf-8",
|
|
191
|
+
)
|
|
192
|
+
finally:
|
|
193
|
+
os.umask(prev_umask)
|
|
194
|
+
except Exception:
|
|
195
|
+
pass
|
|
196
|
+
|
|
165
197
|
entry = {
|
|
166
198
|
"ts": datetime.now(timezone.utc).isoformat(),
|
|
167
199
|
"session_id": session_id,
|
|
@@ -178,6 +210,8 @@ entry = {
|
|
|
178
210
|
"kb_cite_reason": cite_reason,
|
|
179
211
|
"kb_cite_count": cite_count,
|
|
180
212
|
"kb_cite_topic_score": cite_topic_score,
|
|
213
|
+
"meta_tag_check_passed": meta_passed,
|
|
214
|
+
"meta_tag_check_reason": meta_reason,
|
|
181
215
|
"mode": "warn",
|
|
182
216
|
}
|
|
183
217
|
|
|
@@ -393,11 +393,31 @@ if [ -n "$SESSION_ID" ]; then
|
|
|
393
393
|
fi
|
|
394
394
|
fi
|
|
395
395
|
|
|
396
|
+
# ─── Meta-tag nudge (PR30 v2.49.0) ───────────────────────────────────────
|
|
397
|
+
# Mirror of the KB citation nudge but for the [arka:meta] one-liner
|
|
398
|
+
# contract. One-shot; deleted after read.
|
|
399
|
+
_META_TAG_NUDGE=""
|
|
400
|
+
if [ -n "$SESSION_ID" ]; then
|
|
401
|
+
_META_FILE="/tmp/arkaos-meta/${SESSION_ID}.json"
|
|
402
|
+
if [ -f "$_META_FILE" ]; then
|
|
403
|
+
if command -v jq &>/dev/null; then
|
|
404
|
+
_META_PASSED=$(jq -r '.passed' "$_META_FILE" 2>/dev/null)
|
|
405
|
+
_META_SUGGEST=$(jq -r '.suggestion // ""' "$_META_FILE" 2>/dev/null)
|
|
406
|
+
if [ "$_META_PASSED" = "false" ] && [ -n "$_META_SUGGEST" ] && [ "$_META_SUGGEST" != "null" ]; then
|
|
407
|
+
_META_TAG_NUDGE="[arka:suggest] ${_META_SUGGEST}"
|
|
408
|
+
fi
|
|
409
|
+
fi
|
|
410
|
+
rm -f "$_META_FILE" 2>/dev/null
|
|
411
|
+
fi
|
|
412
|
+
fi
|
|
413
|
+
|
|
396
414
|
# ─── Output ──────────────────────────────────────────────────────────────
|
|
397
415
|
_OUT_CONTEXT="${_ARKA_GREETING:-}${_SYNC_NOTICE:-}${_ROUTE_REMINDER}${_WORKFLOW_DIRECTIVE} $python_result"
|
|
398
416
|
[ -n "$_HYGIENE" ] && _OUT_CONTEXT="$_OUT_CONTEXT $_HYGIENE"
|
|
399
417
|
[ -n "$_KB_CITE_NUDGE" ] && _OUT_CONTEXT="$_OUT_CONTEXT
|
|
400
418
|
$_KB_CITE_NUDGE"
|
|
419
|
+
[ -n "$_META_TAG_NUDGE" ] && _OUT_CONTEXT="$_OUT_CONTEXT
|
|
420
|
+
$_META_TAG_NUDGE"
|
|
401
421
|
[ -n "$_ARKA_CONTEXT_HITS" ] && _OUT_CONTEXT="$_OUT_CONTEXT
|
|
402
422
|
$_ARKA_CONTEXT_HITS"
|
|
403
423
|
# Escape for JSON
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""[arka:meta] one-liner soft-block check (PR30 v2.49.0).
|
|
2
|
+
|
|
3
|
+
Response-side classifier. Inspects an assistant response for the
|
|
4
|
+
``[arka:meta] kb=N research=X persona=Y gap=Z critic=W`` one-liner
|
|
5
|
+
established by the session-start hook in PR12 v2.34.0.
|
|
6
|
+
|
|
7
|
+
Soft-block contract — never raises. Hooks consume MetaTagResult and
|
|
8
|
+
decide whether to surface a suggestion. Mirrors the shape of
|
|
9
|
+
``core.governance.kb_cite_check`` (PR18 v2.40.0).
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import re
|
|
15
|
+
from dataclasses import dataclass
|
|
16
|
+
|
|
17
|
+
_META_TAG_RE: re.Pattern[str] = re.compile(r"\[arka:meta\]", re.IGNORECASE)
|
|
18
|
+
_BYPASS_DEFAULTS: tuple[str, ...] = ("[arka:trivial]",)
|
|
19
|
+
_TRIVIAL_WORD_THRESHOLD: int = 15
|
|
20
|
+
_SUGGESTION_TEXT: str = (
|
|
21
|
+
"Meta-tag missing — end substantive responses with a single "
|
|
22
|
+
"`[arka:meta] kb=N research=X persona=Y gap=Z critic=W` line. "
|
|
23
|
+
"Fields: kb=N (notes consulted), research=X (MCPs invoked or "
|
|
24
|
+
"'none'), persona=Y (advisor or 'orchestrator'), gap=Z (KB gap "
|
|
25
|
+
"or 'none'), critic=W (passed|failed|skipped)."
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass(frozen=True)
|
|
30
|
+
class MetaTagResult:
|
|
31
|
+
"""Verdict of a meta-tag check. Immutable; safe to log as JSON."""
|
|
32
|
+
passed: bool
|
|
33
|
+
reason: str
|
|
34
|
+
suggestion: str | None
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def check_meta_tag(
|
|
38
|
+
response_text: str,
|
|
39
|
+
*,
|
|
40
|
+
bypass_markers: tuple[str, ...] = _BYPASS_DEFAULTS,
|
|
41
|
+
) -> MetaTagResult:
|
|
42
|
+
"""Classify whether a response carries the [arka:meta] one-liner.
|
|
43
|
+
|
|
44
|
+
Order matters: a SHORT response *with* the tag still counts as
|
|
45
|
+
`present` — the trivial-length bypass only short-circuits when
|
|
46
|
+
the tag genuinely isn't there.
|
|
47
|
+
"""
|
|
48
|
+
text = response_text or ""
|
|
49
|
+
if _has_bypass_marker(text, bypass_markers):
|
|
50
|
+
return MetaTagResult(True, "trivial", None)
|
|
51
|
+
if _META_TAG_RE.search(text):
|
|
52
|
+
return MetaTagResult(True, "present", None)
|
|
53
|
+
if _is_trivial_length(text):
|
|
54
|
+
return MetaTagResult(True, "trivial", None)
|
|
55
|
+
return MetaTagResult(False, "missing", _SUGGESTION_TEXT)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _has_bypass_marker(text: str, markers: tuple[str, ...]) -> bool:
|
|
59
|
+
return any(marker in text for marker in markers)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _is_trivial_length(text: str) -> bool:
|
|
63
|
+
return len(text.split()) < _TRIVIAL_WORD_THRESHOLD
|
package/package.json
CHANGED