@rubytech/create-realagent-code 0.1.253 → 0.1.255
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/package.json +1 -1
- package/payload/platform/.docs/search-surface-contract.md +58 -0
- package/payload/platform/lib/embed-client/dist/index.d.ts +4 -0
- package/payload/platform/lib/embed-client/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/embed-client/dist/index.js +53 -0
- package/payload/platform/lib/embed-client/dist/index.js.map +1 -0
- package/payload/platform/lib/embed-client/src/index.ts +53 -0
- package/payload/platform/lib/embed-client/tsconfig.json +8 -0
- package/payload/platform/lib/graph-search/dist/index.d.ts +27 -6
- package/payload/platform/lib/graph-search/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/graph-search/dist/index.js +19 -1
- package/payload/platform/lib/graph-search/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-search/src/index.ts +28 -6
- package/payload/platform/lib/graph-write/dist/index.d.ts +25 -0
- package/payload/platform/lib/graph-write/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/graph-write/dist/index.js +78 -2
- package/payload/platform/lib/graph-write/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-write/src/index.ts +96 -1
- package/payload/platform/package.json +2 -2
- package/payload/platform/plugins/admin/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/admin/PLUGIN.md +3 -3
- package/payload/platform/plugins/admin/hooks/__tests__/{session-end-retrospective.test.sh → insight.test.sh} +152 -153
- package/payload/platform/plugins/admin/hooks/insight.sh +219 -0
- package/payload/platform/plugins/admin/hooks/lib/admin-graph-pass-common.sh +5 -5
- package/payload/platform/plugins/admin/mcp/dist/index.js +33 -19
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +23 -23
- package/payload/platform/plugins/docs/references/graph.md +2 -0
- package/payload/platform/plugins/docs/references/internals.md +12 -1
- package/payload/platform/plugins/docs/references/neo4j.md +2 -2
- package/payload/platform/plugins/docs/references/platform.md +1 -1
- package/payload/platform/plugins/docs/references/session-retrospective.md +5 -18
- package/payload/platform/plugins/email/PLUGIN.md +2 -2
- package/payload/platform/plugins/email/mcp/dist/index.js +8 -0
- package/payload/platform/plugins/email/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/attachment-resolve.d.ts +19 -0
- package/payload/platform/plugins/email/mcp/dist/lib/attachment-resolve.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/attachment-resolve.js +64 -0
- package/payload/platform/plugins/email/mcp/dist/lib/attachment-resolve.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/smtp.d.ts +4 -0
- package/payload/platform/plugins/email/mcp/dist/lib/smtp.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/smtp.js +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/smtp.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-reply.d.ts +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-reply.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-reply.js +6 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-reply.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-send.d.ts +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-send.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-send.js +7 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-send.js.map +1 -1
- package/payload/platform/plugins/email/references/email-reference.md +4 -0
- package/payload/platform/plugins/memory/PLUGIN.md +1 -2
- package/payload/platform/plugins/memory/mcp/dist/index.js +5 -43
- package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/document-sectioniser.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/document-sectioniser.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/document-sectioniser.test.js +41 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/document-sectioniser.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/graph-write-embed-net.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/graph-write-embed-net.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/graph-write-embed-net.test.js +90 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/graph-write-embed-net.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/vector-indexed-labels-drift.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/vector-indexed-labels-drift.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/vector-indexed-labels-drift.test.js +27 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/vector-indexed-labels-drift.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/document-sectioniser.d.ts +10 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/document-sectioniser.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/document-sectioniser.js +47 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/document-sectioniser.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/embeddings.d.ts +1 -2
- package/payload/platform/plugins/memory/mcp/dist/lib/embeddings.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/embeddings.js +5 -28
- package/payload/platform/plugins/memory/mcp/dist/lib/embeddings.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/build-text-representation-bound.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/build-text-representation-bound.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/build-text-representation-bound.test.js +20 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/build-text-representation-bound.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-preference-embed.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-preference-embed.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-preference-embed.test.js +67 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-preference-embed.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/embeddings-cap.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/embeddings-cap.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/embeddings-cap.test.js +34 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/embeddings-cap.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-section-writer.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-section-writer.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-section-writer.test.js +61 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-section-writer.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write.test.js +23 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-reindex.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-reindex.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-reindex.test.js +87 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-reindex.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-search-fields.test.js +3 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-search-fields.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-search-threshold.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-search-threshold.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-search-threshold.test.js +34 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-search-threshold.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.js +19 -4
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.d.ts +33 -6
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js +280 -10
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-reindex.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-reindex.js +76 -37
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-reindex.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.js +11 -2
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-typed-edge-pass.d.ts +3 -3
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-typed-edge-pass.js +2 -2
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js +10 -2
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js +6 -3
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/vitest.config.ts +5 -0
- package/payload/platform/plugins/memory/references/schema-base.md +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/ics-graph-ingest.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/ics-graph-ingest.js +15 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/ics-graph-ingest.js.map +1 -1
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-execute.js +4 -0
- package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-execute.js.map +1 -1
- package/payload/platform/scripts/identity-forbidden-token-check.mjs +0 -1
- package/payload/platform/scripts/setup-account.sh +2 -8
- package/payload/platform/services/claude-session-manager/dist/agent-identity-locator.d.ts +23 -0
- package/payload/platform/services/claude-session-manager/dist/agent-identity-locator.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/agent-identity-locator.js +29 -0
- package/payload/platform/services/claude-session-manager/dist/agent-identity-locator.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +0 -1
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +14 -0
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +8 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.d.ts +13 -1
- package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.js +26 -2
- package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +1 -1
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts +12 -3
- package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/system-prompt.js +3 -2
- package/payload/platform/services/claude-session-manager/dist/system-prompt.js.map +1 -1
- package/payload/platform/templates/agents/admin/IDENTITY.md +2 -2
- package/payload/platform/templates/specialists/agents/database-operator.md +2 -6
- package/payload/server/{chunk-M6A6EZD4.js → chunk-76HRO7NX.js} +16 -2
- package/payload/server/maxy-edge.js +1 -1
- package/payload/server/server.js +534 -35
- package/payload/platform/plugins/admin/hooks/session-end-retrospective.sh +0 -214
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# UserPromptSubmit hook — on-demand `/insight`. When the operator types the
|
|
3
|
+
# literal token `/insight` as a standalone message (or a standalone line), the
|
|
4
|
+
# admin agent runs a four-pass review (technical learnings, operator-
|
|
5
|
+
# relationship updates, graph-completeness sweep, typed-edge auto-extraction)
|
|
6
|
+
# over the session so far and calls the deterministic
|
|
7
|
+
# `session-retrospective-mark-complete` MCP sentinel — then the session
|
|
8
|
+
# CONTINUES. Nothing is blocked, terminated, reset, or cleared. Task 634
|
|
9
|
+
# replaced the Task 282 session-end Stop gate with this trigger; the four
|
|
10
|
+
# passes and the sentinel call are byte-for-byte the same process — only the
|
|
11
|
+
# trigger and timing changed.
|
|
12
|
+
#
|
|
13
|
+
# Trigger detection. The token is read from the `prompt` field of the
|
|
14
|
+
# UserPromptSubmit stdin envelope, case-insensitively, matching the whole
|
|
15
|
+
# message (after .strip()) OR a standalone line within it — never embedded in
|
|
16
|
+
# prose. This is the same whole-message / standalone-line discipline the
|
|
17
|
+
# session-end gate applied to its end-intent tokens; it keeps "give me some
|
|
18
|
+
# /insight into the numbers" from false-triggering.
|
|
19
|
+
#
|
|
20
|
+
# Injection. On a match the four-pass instruction is printed to STDOUT.
|
|
21
|
+
# UserPromptSubmit stdout is appended to the turn as additional context, so the
|
|
22
|
+
# agent receives the instruction alongside the operator's `/insight` message.
|
|
23
|
+
# Structured logs go through POST /api/admin/log-ingest (never stdout), so the
|
|
24
|
+
# injected context stays clean. There is no exit-2 path and no stderr
|
|
25
|
+
# instruction — UserPromptSubmit cannot and must not block the turn.
|
|
26
|
+
#
|
|
27
|
+
# Standing reconciliation (no-event failure detector). The run-and-continue
|
|
28
|
+
# model removes the Stop gate that previously forced completion, so the
|
|
29
|
+
# dominant failure mode is "triggered but never completed" — the agent injects
|
|
30
|
+
# the instruction, returns prose, and never calls the sentinel. Because this
|
|
31
|
+
# hook fires on every prompt, it scans the transcript (`transcript_path`) each
|
|
32
|
+
# invocation for the latest real-user turn whose text matches `/insight`; when
|
|
33
|
+
# no `session-retrospective-mark-complete` tool_use appears after that turn it
|
|
34
|
+
# emits `[insight] prior-incomplete sessionId=<id> triggered-turn=<idx>`. This
|
|
35
|
+
# surfaces an unfinished pass on the next prompt without waiting for session
|
|
36
|
+
# end and without reproducing the failure.
|
|
37
|
+
# [[feedback_no_stdout_parsing_for_control_flow]] — the trigger is a literal
|
|
38
|
+
# token match, never prose parsing.
|
|
39
|
+
#
|
|
40
|
+
# Gating (common guards live in `lib/admin-graph-pass-common.sh`):
|
|
41
|
+
# - role-not-admin / is-specialist / empty-stdin / missing-transcript:
|
|
42
|
+
# handled by the shared helper, emit `trigger-skipped` and exit 0.
|
|
43
|
+
# - no-insight-token: the prompt carries no standalone `/insight` token,
|
|
44
|
+
# emit `trigger-skipped` and exit 0.
|
|
45
|
+
#
|
|
46
|
+
# Input: Claude Code's UserPromptSubmit stdin shape
|
|
47
|
+
# { "session_id": "<id>", "transcript_path": "<jsonl path>",
|
|
48
|
+
# "prompt": "<operator message>", "hook_event_name": "UserPromptSubmit" }
|
|
49
|
+
|
|
50
|
+
set -uo pipefail
|
|
51
|
+
|
|
52
|
+
AGP_LOG_TAG="insight"
|
|
53
|
+
AGP_LIB="$(dirname "$0")/lib/admin-graph-pass-common.sh"
|
|
54
|
+
if [ ! -f "$AGP_LIB" ]; then
|
|
55
|
+
echo "[${AGP_LOG_TAG}] trigger-skipped sessionId=unknown reason=missing-helper path=${AGP_LIB}" >&2
|
|
56
|
+
exit 0
|
|
57
|
+
fi
|
|
58
|
+
# shellcheck source=lib/admin-graph-pass-common.sh
|
|
59
|
+
source "$AGP_LIB"
|
|
60
|
+
|
|
61
|
+
agp_setup_logging
|
|
62
|
+
agp_read_stdin
|
|
63
|
+
agp_parse_stdin
|
|
64
|
+
agp_guard_role_specialist
|
|
65
|
+
agp_guard_transcript require-stdin
|
|
66
|
+
|
|
67
|
+
# Extract the operator's raw prompt from the envelope. Empty when absent.
|
|
68
|
+
PROMPT=$(printf '%s' "$INPUT" | python3 -c '
|
|
69
|
+
import sys, json
|
|
70
|
+
try:
|
|
71
|
+
d = json.load(sys.stdin)
|
|
72
|
+
p = d.get("prompt", "") or ""
|
|
73
|
+
sys.stdout.write(p if isinstance(p, str) else "")
|
|
74
|
+
except Exception:
|
|
75
|
+
pass
|
|
76
|
+
' 2>/dev/null)
|
|
77
|
+
|
|
78
|
+
# Single Python pass reports two facts on one line:
|
|
79
|
+
# 1. Whether PROMPT carries a standalone `/insight` token → token or empty.
|
|
80
|
+
# 2. Reconciliation: the index of the latest real-user transcript turn whose
|
|
81
|
+
# text matches `/insight` that has NO subsequent
|
|
82
|
+
# `session-retrospective-mark-complete` tool_use → index or empty.
|
|
83
|
+
# Output: <trigger-token-or-empty>\t<prior-incomplete-idx-or-empty>
|
|
84
|
+
INSPECTION=$(PROMPT="$PROMPT" TRANSCRIPT_PATH="$TRANSCRIPT_PATH" python3 - <<'PY'
|
|
85
|
+
import os, json
|
|
86
|
+
|
|
87
|
+
SENTINEL_TOOL = "mcp__admin__session-retrospective-mark-complete"
|
|
88
|
+
TOKEN = "/insight"
|
|
89
|
+
|
|
90
|
+
def text_has_standalone_token(text):
|
|
91
|
+
# Case-insensitive: token is the whole message (after strip) OR a
|
|
92
|
+
# standalone line within it. Never matches the token embedded in prose.
|
|
93
|
+
if not text:
|
|
94
|
+
return False
|
|
95
|
+
haystacks = {text.strip().lower()}
|
|
96
|
+
for line in text.splitlines():
|
|
97
|
+
s = line.strip().lower()
|
|
98
|
+
if s:
|
|
99
|
+
haystacks.add(s)
|
|
100
|
+
return TOKEN in haystacks
|
|
101
|
+
|
|
102
|
+
def is_real_user(rec):
|
|
103
|
+
if not isinstance(rec, dict) or rec.get("type") != "user":
|
|
104
|
+
return False
|
|
105
|
+
msg = rec.get("message")
|
|
106
|
+
if not isinstance(msg, dict) or msg.get("role") != "user":
|
|
107
|
+
return False
|
|
108
|
+
content = msg.get("content")
|
|
109
|
+
if isinstance(content, str):
|
|
110
|
+
return True
|
|
111
|
+
if isinstance(content, list):
|
|
112
|
+
for b in content:
|
|
113
|
+
if isinstance(b, dict) and b.get("type") != "tool_result":
|
|
114
|
+
return True
|
|
115
|
+
return False
|
|
116
|
+
return True
|
|
117
|
+
|
|
118
|
+
def extract_user_text(rec):
|
|
119
|
+
msg = rec.get("message", {}) or {}
|
|
120
|
+
content = msg.get("content")
|
|
121
|
+
if isinstance(content, str):
|
|
122
|
+
return content
|
|
123
|
+
if isinstance(content, list):
|
|
124
|
+
out = []
|
|
125
|
+
for b in content:
|
|
126
|
+
if isinstance(b, dict) and b.get("type") == "text":
|
|
127
|
+
t = b.get("text")
|
|
128
|
+
if isinstance(t, str):
|
|
129
|
+
out.append(t)
|
|
130
|
+
return "".join(out)
|
|
131
|
+
return ""
|
|
132
|
+
|
|
133
|
+
def is_assistant(rec):
|
|
134
|
+
return (isinstance(rec, dict) and rec.get("type") == "assistant"
|
|
135
|
+
and isinstance(rec.get("message"), dict)
|
|
136
|
+
and rec["message"].get("role") == "assistant")
|
|
137
|
+
|
|
138
|
+
def assistant_tool_use_names(rec):
|
|
139
|
+
if not is_assistant(rec):
|
|
140
|
+
return []
|
|
141
|
+
content = rec["message"].get("content")
|
|
142
|
+
if not isinstance(content, list):
|
|
143
|
+
return []
|
|
144
|
+
return [b.get("name") for b in content
|
|
145
|
+
if isinstance(b, dict) and b.get("type") == "tool_use"
|
|
146
|
+
and isinstance(b.get("name"), str)]
|
|
147
|
+
|
|
148
|
+
# Trigger — read from the prompt field, not the transcript.
|
|
149
|
+
trigger = TOKEN if text_has_standalone_token(os.environ.get("PROMPT", "")) else ""
|
|
150
|
+
|
|
151
|
+
# Reconciliation — scan the transcript for the latest real-user `/insight`
|
|
152
|
+
# turn and check whether a sentinel tool_use follows it.
|
|
153
|
+
records = []
|
|
154
|
+
try:
|
|
155
|
+
with open(os.environ["TRANSCRIPT_PATH"], "r", encoding="utf-8") as f:
|
|
156
|
+
for raw in f:
|
|
157
|
+
raw = raw.strip()
|
|
158
|
+
if not raw:
|
|
159
|
+
continue
|
|
160
|
+
try:
|
|
161
|
+
records.append(json.loads(raw))
|
|
162
|
+
except Exception:
|
|
163
|
+
continue
|
|
164
|
+
except Exception:
|
|
165
|
+
records = []
|
|
166
|
+
|
|
167
|
+
latest_insight_idx = -1
|
|
168
|
+
for i in range(len(records) - 1, -1, -1):
|
|
169
|
+
if is_real_user(records[i]) and text_has_standalone_token(extract_user_text(records[i])):
|
|
170
|
+
latest_insight_idx = i
|
|
171
|
+
break
|
|
172
|
+
|
|
173
|
+
prior_incomplete = ""
|
|
174
|
+
if latest_insight_idx != -1:
|
|
175
|
+
after = records[latest_insight_idx + 1:]
|
|
176
|
+
# "Triggered but never completed" means the agent RESPONDED to the
|
|
177
|
+
# `/insight` turn (≥1 assistant record after it) yet never called the
|
|
178
|
+
# sentinel. Requiring an assistant record excludes the just-submitted
|
|
179
|
+
# `/insight` turn — which has no response yet — so a genuine trigger
|
|
180
|
+
# never self-reports as incomplete, regardless of whether Claude Code
|
|
181
|
+
# has already written the current prompt to the transcript.
|
|
182
|
+
assistant_after = any(is_assistant(r) for r in after)
|
|
183
|
+
sentinel_after = any(SENTINEL_TOOL in assistant_tool_use_names(r) for r in after)
|
|
184
|
+
if assistant_after and not sentinel_after:
|
|
185
|
+
prior_incomplete = str(latest_insight_idx)
|
|
186
|
+
|
|
187
|
+
print(f"{trigger}\t{prior_incomplete}")
|
|
188
|
+
PY
|
|
189
|
+
)
|
|
190
|
+
TRIGGER_TOKEN="${INSPECTION%% *}"
|
|
191
|
+
PRIOR_INCOMPLETE_IDX="${INSPECTION#* }"
|
|
192
|
+
|
|
193
|
+
# Standing reconciliation emit — independent of the current trigger.
|
|
194
|
+
if [ -n "$PRIOR_INCOMPLETE_IDX" ]; then
|
|
195
|
+
agp_emit_log "prior-incomplete sessionId=${SESSION_ID} triggered-turn=${PRIOR_INCOMPLETE_IDX}"
|
|
196
|
+
fi
|
|
197
|
+
|
|
198
|
+
if [ -z "$TRIGGER_TOKEN" ]; then
|
|
199
|
+
agp_emit_log "trigger-skipped sessionId=${SESSION_ID} reason=no-insight-token"
|
|
200
|
+
exit 0
|
|
201
|
+
fi
|
|
202
|
+
|
|
203
|
+
agp_emit_log "trigger sessionId=${SESSION_ID} token=${TRIGGER_TOKEN}"
|
|
204
|
+
|
|
205
|
+
cat <<'INSTRUCTION'
|
|
206
|
+
The operator requested an insight pass — run the four passes now over the session so far, then keep going in this same session. Do NOT call `session-reset`, do NOT clear the context, do NOT end the session: the conversation continues afterward and the operator (or a parallel session) keeps working against the same live state. Use the tools you already have.
|
|
207
|
+
|
|
208
|
+
Pass 1 — Technical learnings. Walk this session for operator corrections and self-detected mistakes. For each one, delegate one graph write to `database-operator` via the Task tool, persisting a Learning or Correction node per the schema. Count what you write.
|
|
209
|
+
|
|
210
|
+
Pass 2 — Operator-relationship updates. Walk this session for tonal signals, rejected phrasings, and working-style preferences. For each one worth carrying forward, Write the observation into `SOUL.md` at the account-scoped path. Count what you write.
|
|
211
|
+
|
|
212
|
+
Pass 3 — Graph completeness sweep. Walk this session for any node, edge, or commitment that was discussed but not written to the graph in-flight. For each missing write, delegate to `database-operator` via the Task tool. Count what you write.
|
|
213
|
+
|
|
214
|
+
Pass 4 — Typed-edge auto-extraction. Delegate to `database-operator` via the Task tool with a prompt naming the typed-edge pass and the session-start timestamp. The specialist calls `memory-typed-edge-pass` with that timestamp and returns the counters. Capture `nodesProcessed` and the accepted count from its reply.
|
|
215
|
+
|
|
216
|
+
Then call `session-retrospective-mark-complete` with all five counts (learningsCount, preferencesCount, graphWriteCount, typedEdgesCount, nodesProcessed) and reply with one short paragraph in your house voice summarising what you wrote — then carry on with the operator's work.
|
|
217
|
+
INSTRUCTION
|
|
218
|
+
|
|
219
|
+
exit 0
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# Shared plumbing for the admin-side hook that orchestrates admin-walked
|
|
3
|
-
# graph writes: `
|
|
4
|
-
# Originally introduced (Task 439) as shared plumbing across two hooks; the
|
|
3
|
+
# graph writes: `insight.sh` (UserPromptSubmit on the operator's `/insight`
|
|
4
|
+
# token). Originally introduced (Task 439) as shared plumbing across two hooks; the
|
|
5
5
|
# per-turn sibling was removed in Task 447 because session-end Pass 3 already
|
|
6
6
|
# performs the graph-completeness sweep. The library is retained — its
|
|
7
7
|
# stdin parse, role / specialist / transcript guards, log-ingest envelope,
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
#
|
|
11
11
|
# Caller contract:
|
|
12
12
|
# 1. Set `AGP_LOG_TAG` to the hook's identifying tag before sourcing.
|
|
13
|
-
# `
|
|
13
|
+
# `insight` is the current value; the helper itself
|
|
14
14
|
# does not log under its own tag.
|
|
15
15
|
# 2. Source this file.
|
|
16
16
|
# 3. Call `agp_setup_logging`, `agp_read_stdin`, `agp_parse_stdin`,
|
|
@@ -122,7 +122,7 @@ agp_guard_role_specialist() {
|
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
# Usage: agp_guard_transcript [require-stdin]
|
|
125
|
-
# Pass `require-stdin` (
|
|
125
|
+
# Pass `require-stdin` (insight.sh) to emit `empty-stdin` on a
|
|
126
126
|
# blank `INPUT`. Without the flag, parse failure on a non-empty INPUT
|
|
127
127
|
# collapses to `missing-transcript` cleanly.
|
|
128
128
|
agp_guard_transcript() {
|
|
@@ -138,7 +138,7 @@ agp_guard_transcript() {
|
|
|
138
138
|
|
|
139
139
|
# Walks $TRANSCRIPT_PATH and reports, in one line on stdout:
|
|
140
140
|
# <real-user-present:yes|no>\t<matched-tool-name-or-empty>
|
|
141
|
-
# A "real user" record matches `
|
|
141
|
+
# A "real user" record matches `insight.sh`'s `is_real_user`
|
|
142
142
|
# — `type=user, message.role=user`, content is a string OR a list with at
|
|
143
143
|
# least one non-`tool_result` block. `tool_result`-only user records are tool
|
|
144
144
|
# replies, never turn starts.
|
|
@@ -9,6 +9,7 @@ import { resolve, join } from "node:path";
|
|
|
9
9
|
import { execFileSync } from "node:child_process";
|
|
10
10
|
import { appendFileSync, cpSync, existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
11
11
|
import { writeAdminEntry, removeAdminFromAccount } from "../../../../lib/admins-write/dist/index.js";
|
|
12
|
+
import { embed } from "../../../../lib/embed-client/dist/index.js";
|
|
12
13
|
import { requirePortEnv } from "../../../../lib/require-port-env/dist/index.js";
|
|
13
14
|
import { deviceUrlBlock } from "../../../../lib/device-url/dist/index.js";
|
|
14
15
|
import { substituteBrandPlaceholders } from "../../../../lib/brand-templating/dist/index.js";
|
|
@@ -913,6 +914,17 @@ eagerTool(server, "admin-add", "Add a new admin user to this account. Creates a
|
|
|
913
914
|
const firstSpace = trimmedName.search(/\s/);
|
|
914
915
|
const givenName = firstSpace === -1 ? trimmedName : trimmedName.slice(0, firstSpace).trim();
|
|
915
916
|
const familyName = firstSpace === -1 ? null : (trimmedName.slice(firstSpace + 1).trim() || null);
|
|
917
|
+
// Task 636 — :Person is vector-indexed; embed the admin-personal Person
|
|
918
|
+
// at write time so it is findable by semantic memory-search, not only
|
|
919
|
+
// after a reindex. A failed embed sets null (audit catches it) and the
|
|
920
|
+
// write proceeds — embedding must never block PIN setup.
|
|
921
|
+
let personEmbedding = null;
|
|
922
|
+
try {
|
|
923
|
+
personEmbedding = await embed(`[Person] ${[givenName, familyName].filter(Boolean).join(" ")} role: admin-personal`);
|
|
924
|
+
}
|
|
925
|
+
catch (err) {
|
|
926
|
+
process.stderr.write(`[graph-write] warn reason=embed-failed labels=Person agent=admin-add detail=${err instanceof Error ? err.message : String(err)}\n`);
|
|
927
|
+
}
|
|
916
928
|
// stamp `accountId` on every AdminUser at MERGE time, both
|
|
917
929
|
// ON CREATE and ON MATCH (the latter via COALESCE so a pre-existing
|
|
918
930
|
// value isn't overwritten if it differs — which would itself be a
|
|
@@ -949,12 +961,13 @@ eagerTool(server, "admin-add", "Add a new admin user to this account. Creates a
|
|
|
949
961
|
familyName: $familyName,
|
|
950
962
|
role: 'admin-personal',
|
|
951
963
|
scope: 'admin',
|
|
964
|
+
embedding: $personEmbedding,
|
|
952
965
|
createdAt: $createdAt
|
|
953
966
|
})
|
|
954
967
|
MERGE (au)-[:OWNS]->(newPerson)
|
|
955
968
|
RETURN false AS reused
|
|
956
969
|
}
|
|
957
|
-
RETURN reused`, { userId, name: trimmedName, createdAt, accountId: ACCOUNT_ID, givenName, familyName });
|
|
970
|
+
RETURN reused`, { userId, name: trimmedName, createdAt, accountId: ACCOUNT_ID, givenName, familyName, personEmbedding });
|
|
958
971
|
if (result.records.length > 0) {
|
|
959
972
|
personReused = result.records[0].get("reused");
|
|
960
973
|
}
|
|
@@ -3089,27 +3102,28 @@ eagerTool(server, "action-edit", "Edit a pending action's input and then execute
|
|
|
3089
3102
|
}
|
|
3090
3103
|
});
|
|
3091
3104
|
// ===================================================================
|
|
3092
|
-
//
|
|
3105
|
+
// Insight-pass completion sentinel (Task 282, retriggered on-demand by Task 634)
|
|
3093
3106
|
// ===================================================================
|
|
3094
3107
|
//
|
|
3095
|
-
// Deterministic completion signal for the
|
|
3096
|
-
//
|
|
3097
|
-
// hook
|
|
3098
|
-
//
|
|
3099
|
-
//
|
|
3108
|
+
// Deterministic completion signal for the on-demand `/insight` pass
|
|
3109
|
+
// (`platform/plugins/admin/hooks/insight.sh`, a UserPromptSubmit hook). The
|
|
3110
|
+
// hook injects the four-pass instruction when the operator types `/insight`;
|
|
3111
|
+
// this tool_use is the agent's record that all four passes ran. Calling it
|
|
3112
|
+
// does not block, release, or end anything — the session continues. Its
|
|
3113
|
+
// absence after an `/insight` turn is what the hook's standing reconciliation
|
|
3114
|
+
// surfaces as `[insight] prior-incomplete`
|
|
3100
3115
|
// ([[feedback_doctrine_paragraph_is_not_a_gate]]).
|
|
3101
3116
|
//
|
|
3102
3117
|
// The tool's only side-effect is one structured log line through
|
|
3103
3118
|
// /api/admin/log-ingest so server.log carries the full chain
|
|
3104
|
-
// trigger →
|
|
3105
|
-
// against one operator sessionId.
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
preferencesCount: z.number().int().nonnegative().describe("SOUL.md writes you made for tonal / working-style observations in this retrospective."),
|
|
3119
|
+
// trigger → complete (and, on failure, trigger with no following complete)
|
|
3120
|
+
// against one operator sessionId.
|
|
3121
|
+
server.tool("session-retrospective-mark-complete", "Marks an on-demand insight pass complete. Call this AFTER walking the session for technical learnings, operator-relationship updates, missing graph writes, and the typed-edge auto-extraction pass — and persisting each one (Task-tool delegations to database-operator for graph writes and the typed-edge pass, Write to SOUL.md for tonal updates). The counts you pass are the number of items you actually persisted in each pass. Calling it records that the insight pass ran; it does not block or end the session — keep working afterward.", {
|
|
3122
|
+
learningsCount: z.number().int().nonnegative().describe("Technical-learning / correction nodes you delegated to database-operator in this insight pass."),
|
|
3123
|
+
preferencesCount: z.number().int().nonnegative().describe("SOUL.md writes you made for tonal / working-style observations in this insight pass."),
|
|
3110
3124
|
graphWriteCount: z.number().int().nonnegative().describe("Graph nodes/edges/commitments you delegated to database-operator for content that surfaced in the session but was not written in-flight."),
|
|
3111
|
-
typedEdgesCount: z.number().int().nonnegative().describe("Typed edges accepted (MERGEd) by the typed-edge auto-extraction pass that database-operator ran in this
|
|
3112
|
-
nodesProcessed: z.number().int().nonnegative().describe("Prose-bearing nodes the typed-edge pass enumerated and scanned in this
|
|
3125
|
+
typedEdgesCount: z.number().int().nonnegative().describe("Typed edges accepted (MERGEd) by the typed-edge auto-extraction pass that database-operator ran in this insight pass. 0 is valid (no prose nodes touched or none yielded edges)."),
|
|
3126
|
+
nodesProcessed: z.number().int().nonnegative().describe("Prose-bearing nodes the typed-edge pass enumerated and scanned in this insight pass. 0 is valid (no prose nodes since the session started)."),
|
|
3113
3127
|
}, async ({ learningsCount, preferencesCount, graphWriteCount, typedEdgesCount, nodesProcessed }) => {
|
|
3114
3128
|
const sessionId = process.env.SESSION_ID || "unknown";
|
|
3115
3129
|
const line = `complete sessionId=${sessionId} learningsCount=${learningsCount} preferencesCount=${preferencesCount} graphWriteCount=${graphWriteCount} typedEdgesCount=${typedEdgesCount} nodesProcessed=${nodesProcessed}`;
|
|
@@ -3120,7 +3134,7 @@ server.tool("session-retrospective-mark-complete", "Marks the end-of-session ret
|
|
|
3120
3134
|
// JSONL tool_use entry, not this POST — so we still return success
|
|
3121
3135
|
// to the caller and emit the line on stderr so it lands in the
|
|
3122
3136
|
// MCP server's stderr log instead of disappearing into a NaN URL.
|
|
3123
|
-
console.error(`[
|
|
3137
|
+
console.error(`[insight] log-ingest-skipped sessionId=${sessionId} reason=missing-env env=PLATFORM_PORT line=${JSON.stringify(line)}`);
|
|
3124
3138
|
}
|
|
3125
3139
|
else {
|
|
3126
3140
|
try {
|
|
@@ -3128,7 +3142,7 @@ server.tool("session-retrospective-mark-complete", "Marks the end-of-session ret
|
|
|
3128
3142
|
await fetch(`http://127.0.0.1:${port}/api/admin/log-ingest`, {
|
|
3129
3143
|
method: "POST",
|
|
3130
3144
|
headers: { "Content-Type": "application/json" },
|
|
3131
|
-
body: JSON.stringify({ tag: "
|
|
3145
|
+
body: JSON.stringify({ tag: "insight", level: "info", line }),
|
|
3132
3146
|
signal: AbortSignal.timeout(2000),
|
|
3133
3147
|
});
|
|
3134
3148
|
}
|
|
@@ -3137,13 +3151,13 @@ server.tool("session-retrospective-mark-complete", "Marks the end-of-session ret
|
|
|
3137
3151
|
// stderr log even when the loopback POST fails. The hook's
|
|
3138
3152
|
// release decision is JSONL-based and does not depend on this
|
|
3139
3153
|
// POST succeeding.
|
|
3140
|
-
console.error(`[
|
|
3154
|
+
console.error(`[insight] log-ingest-failed sessionId=${sessionId} err=${err instanceof Error ? err.message : String(err)}`);
|
|
3141
3155
|
}
|
|
3142
3156
|
}
|
|
3143
3157
|
return {
|
|
3144
3158
|
content: [{
|
|
3145
3159
|
type: "text",
|
|
3146
|
-
text: `
|
|
3160
|
+
text: `Insight pass marked complete (learnings=${learningsCount} preferences=${preferencesCount} graphWrites=${graphWriteCount} typedEdges=${typedEdgesCount} nodesProcessed=${nodesProcessed}). The session continues — carry on with the operator's work.`,
|
|
3147
3161
|
}],
|
|
3148
3162
|
};
|
|
3149
3163
|
});
|