@rubytech/create-realagent-code 0.1.254 → 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.
Files changed (158) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/.docs/search-surface-contract.md +58 -0
  3. package/payload/platform/lib/embed-client/dist/index.d.ts +4 -0
  4. package/payload/platform/lib/embed-client/dist/index.d.ts.map +1 -0
  5. package/payload/platform/lib/embed-client/dist/index.js +53 -0
  6. package/payload/platform/lib/embed-client/dist/index.js.map +1 -0
  7. package/payload/platform/lib/embed-client/src/index.ts +53 -0
  8. package/payload/platform/lib/embed-client/tsconfig.json +8 -0
  9. package/payload/platform/lib/graph-search/dist/index.d.ts +27 -6
  10. package/payload/platform/lib/graph-search/dist/index.d.ts.map +1 -1
  11. package/payload/platform/lib/graph-search/dist/index.js +19 -1
  12. package/payload/platform/lib/graph-search/dist/index.js.map +1 -1
  13. package/payload/platform/lib/graph-search/src/index.ts +28 -6
  14. package/payload/platform/lib/graph-write/dist/index.d.ts +25 -0
  15. package/payload/platform/lib/graph-write/dist/index.d.ts.map +1 -1
  16. package/payload/platform/lib/graph-write/dist/index.js +78 -2
  17. package/payload/platform/lib/graph-write/dist/index.js.map +1 -1
  18. package/payload/platform/lib/graph-write/src/index.ts +96 -1
  19. package/payload/platform/package.json +2 -2
  20. package/payload/platform/plugins/admin/.claude-plugin/plugin.json +1 -1
  21. package/payload/platform/plugins/admin/PLUGIN.md +3 -3
  22. package/payload/platform/plugins/admin/hooks/__tests__/{session-end-retrospective.test.sh → insight.test.sh} +152 -153
  23. package/payload/platform/plugins/admin/hooks/insight.sh +219 -0
  24. package/payload/platform/plugins/admin/hooks/lib/admin-graph-pass-common.sh +5 -5
  25. package/payload/platform/plugins/admin/mcp/dist/index.js +33 -19
  26. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  27. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +23 -23
  28. package/payload/platform/plugins/docs/references/graph.md +2 -0
  29. package/payload/platform/plugins/docs/references/internals.md +12 -1
  30. package/payload/platform/plugins/docs/references/neo4j.md +2 -2
  31. package/payload/platform/plugins/docs/references/platform.md +1 -1
  32. package/payload/platform/plugins/docs/references/session-retrospective.md +5 -18
  33. package/payload/platform/plugins/email/PLUGIN.md +2 -2
  34. package/payload/platform/plugins/email/mcp/dist/index.js +8 -0
  35. package/payload/platform/plugins/email/mcp/dist/index.js.map +1 -1
  36. package/payload/platform/plugins/email/mcp/dist/lib/attachment-resolve.d.ts +19 -0
  37. package/payload/platform/plugins/email/mcp/dist/lib/attachment-resolve.d.ts.map +1 -0
  38. package/payload/platform/plugins/email/mcp/dist/lib/attachment-resolve.js +64 -0
  39. package/payload/platform/plugins/email/mcp/dist/lib/attachment-resolve.js.map +1 -0
  40. package/payload/platform/plugins/email/mcp/dist/lib/smtp.d.ts +4 -0
  41. package/payload/platform/plugins/email/mcp/dist/lib/smtp.d.ts.map +1 -1
  42. package/payload/platform/plugins/email/mcp/dist/lib/smtp.js +1 -0
  43. package/payload/platform/plugins/email/mcp/dist/lib/smtp.js.map +1 -1
  44. package/payload/platform/plugins/email/mcp/dist/tools/email-reply.d.ts +1 -0
  45. package/payload/platform/plugins/email/mcp/dist/tools/email-reply.d.ts.map +1 -1
  46. package/payload/platform/plugins/email/mcp/dist/tools/email-reply.js +6 -1
  47. package/payload/platform/plugins/email/mcp/dist/tools/email-reply.js.map +1 -1
  48. package/payload/platform/plugins/email/mcp/dist/tools/email-send.d.ts +1 -0
  49. package/payload/platform/plugins/email/mcp/dist/tools/email-send.d.ts.map +1 -1
  50. package/payload/platform/plugins/email/mcp/dist/tools/email-send.js +7 -1
  51. package/payload/platform/plugins/email/mcp/dist/tools/email-send.js.map +1 -1
  52. package/payload/platform/plugins/email/references/email-reference.md +4 -0
  53. package/payload/platform/plugins/memory/PLUGIN.md +1 -2
  54. package/payload/platform/plugins/memory/mcp/dist/index.js +5 -43
  55. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
  56. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/document-sectioniser.test.d.ts +2 -0
  57. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/document-sectioniser.test.d.ts.map +1 -0
  58. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/document-sectioniser.test.js +41 -0
  59. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/document-sectioniser.test.js.map +1 -0
  60. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/graph-write-embed-net.test.d.ts +2 -0
  61. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/graph-write-embed-net.test.d.ts.map +1 -0
  62. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/graph-write-embed-net.test.js +90 -0
  63. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/graph-write-embed-net.test.js.map +1 -0
  64. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/vector-indexed-labels-drift.test.d.ts +2 -0
  65. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/vector-indexed-labels-drift.test.d.ts.map +1 -0
  66. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/vector-indexed-labels-drift.test.js +27 -0
  67. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/vector-indexed-labels-drift.test.js.map +1 -0
  68. package/payload/platform/plugins/memory/mcp/dist/lib/document-sectioniser.d.ts +10 -0
  69. package/payload/platform/plugins/memory/mcp/dist/lib/document-sectioniser.d.ts.map +1 -0
  70. package/payload/platform/plugins/memory/mcp/dist/lib/document-sectioniser.js +47 -0
  71. package/payload/platform/plugins/memory/mcp/dist/lib/document-sectioniser.js.map +1 -0
  72. package/payload/platform/plugins/memory/mcp/dist/lib/embeddings.d.ts +1 -2
  73. package/payload/platform/plugins/memory/mcp/dist/lib/embeddings.d.ts.map +1 -1
  74. package/payload/platform/plugins/memory/mcp/dist/lib/embeddings.js +5 -28
  75. package/payload/platform/plugins/memory/mcp/dist/lib/embeddings.js.map +1 -1
  76. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/build-text-representation-bound.test.d.ts +2 -0
  77. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/build-text-representation-bound.test.d.ts.map +1 -0
  78. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/build-text-representation-bound.test.js +20 -0
  79. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/build-text-representation-bound.test.js.map +1 -0
  80. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-preference-embed.test.d.ts +2 -0
  81. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-preference-embed.test.d.ts.map +1 -0
  82. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-preference-embed.test.js +67 -0
  83. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-preference-embed.test.js.map +1 -0
  84. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/embeddings-cap.test.d.ts +2 -0
  85. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/embeddings-cap.test.d.ts.map +1 -0
  86. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/embeddings-cap.test.js +34 -0
  87. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/embeddings-cap.test.js.map +1 -0
  88. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-section-writer.test.d.ts +2 -0
  89. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-section-writer.test.d.ts.map +1 -0
  90. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-section-writer.test.js +61 -0
  91. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-section-writer.test.js.map +1 -0
  92. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write.test.js +23 -1
  93. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-archive-write.test.js.map +1 -1
  94. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-reindex.test.d.ts +2 -0
  95. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-reindex.test.d.ts.map +1 -0
  96. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-reindex.test.js +87 -0
  97. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-reindex.test.js.map +1 -0
  98. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-search-fields.test.js +3 -0
  99. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-search-fields.test.js.map +1 -1
  100. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-search-threshold.test.d.ts +2 -0
  101. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-search-threshold.test.d.ts.map +1 -0
  102. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-search-threshold.test.js +34 -0
  103. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-search-threshold.test.js.map +1 -0
  104. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.d.ts.map +1 -1
  105. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.js +19 -4
  106. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.js.map +1 -1
  107. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.d.ts +33 -6
  108. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.d.ts.map +1 -1
  109. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js +280 -10
  110. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js.map +1 -1
  111. package/payload/platform/plugins/memory/mcp/dist/tools/memory-reindex.d.ts.map +1 -1
  112. package/payload/platform/plugins/memory/mcp/dist/tools/memory-reindex.js +76 -37
  113. package/payload/platform/plugins/memory/mcp/dist/tools/memory-reindex.js.map +1 -1
  114. package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.d.ts.map +1 -1
  115. package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.js +11 -2
  116. package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.js.map +1 -1
  117. package/payload/platform/plugins/memory/mcp/dist/tools/memory-typed-edge-pass.d.ts +3 -3
  118. package/payload/platform/plugins/memory/mcp/dist/tools/memory-typed-edge-pass.js +2 -2
  119. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts.map +1 -1
  120. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js +10 -2
  121. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js.map +1 -1
  122. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js +6 -3
  123. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js.map +1 -1
  124. package/payload/platform/plugins/memory/mcp/vitest.config.ts +5 -0
  125. package/payload/platform/plugins/memory/references/schema-base.md +1 -1
  126. package/payload/platform/plugins/scheduling/mcp/dist/lib/ics-graph-ingest.d.ts.map +1 -1
  127. package/payload/platform/plugins/scheduling/mcp/dist/lib/ics-graph-ingest.js +15 -0
  128. package/payload/platform/plugins/scheduling/mcp/dist/lib/ics-graph-ingest.js.map +1 -1
  129. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-execute.js +4 -0
  130. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-execute.js.map +1 -1
  131. package/payload/platform/scripts/identity-forbidden-token-check.mjs +0 -1
  132. package/payload/platform/scripts/setup-account.sh +2 -8
  133. package/payload/platform/services/claude-session-manager/dist/agent-identity-locator.d.ts +23 -0
  134. package/payload/platform/services/claude-session-manager/dist/agent-identity-locator.d.ts.map +1 -0
  135. package/payload/platform/services/claude-session-manager/dist/agent-identity-locator.js +29 -0
  136. package/payload/platform/services/claude-session-manager/dist/agent-identity-locator.js.map +1 -0
  137. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
  138. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +0 -1
  139. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  140. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  141. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +8 -1
  142. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
  143. package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.d.ts +13 -1
  144. package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.d.ts.map +1 -1
  145. package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.js +26 -2
  146. package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.js.map +1 -1
  147. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +1 -1
  148. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
  149. package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts +12 -3
  150. package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts.map +1 -1
  151. package/payload/platform/services/claude-session-manager/dist/system-prompt.js +3 -2
  152. package/payload/platform/services/claude-session-manager/dist/system-prompt.js.map +1 -1
  153. package/payload/platform/templates/agents/admin/IDENTITY.md +2 -2
  154. package/payload/platform/templates/specialists/agents/database-operator.md +2 -6
  155. package/payload/server/{chunk-M6A6EZD4.js → chunk-76HRO7NX.js} +16 -2
  156. package/payload/server/maxy-edge.js +1 -1
  157. package/payload/server/server.js +473 -28
  158. 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: `session-end-retrospective.sh` (Stop on operator end-intent).
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
- # `session-retrospective` is the current value; the helper itself
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` (session-end-retrospective) to emit `empty-stdin` on a
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 `session-end-retrospective.sh`'s `is_real_user`
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
- // Session-end retrospective sentinel (Task 282)
3105
+ // Insight-pass completion sentinel (Task 282, retriggered on-demand by Task 634)
3093
3106
  // ===================================================================
3094
3107
  //
3095
- // Deterministic completion signal for the session-end retrospective gate
3096
- // hook (`platform/plugins/admin/hooks/session-end-retrospective.sh`). The
3097
- // hook walks the operator's JSONL for any `tool_use` of THIS tool's exact
3098
- // name and, when present, releases the Stop gate. Prose retrospectives
3099
- // without this call leave the gate blocked
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 → gate-blocked complete gate-released
3105
- // against one operator sessionId. The release decision itself is the
3106
- // JSONL grepnever the log line.
3107
- server.tool("session-retrospective-mark-complete", "Marks the end-of-session retrospective 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. The session-end Stop hook gate releases on the presence of this tool_use in the transcript — calling it is the deterministic signal that the retrospective ran.", {
3108
- learningsCount: z.number().int().nonnegative().describe("Technical-learning / correction nodes you delegated to database-operator in this retrospective."),
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 retrospective. 0 is valid (no prose nodes touched or none yielded edges)."),
3112
- nodesProcessed: z.number().int().nonnegative().describe("Prose-bearing nodes the typed-edge pass enumerated and scanned in this retrospective. 0 is valid (no prose nodes since the session started)."),
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(`[session-retrospective] log-ingest-skipped sessionId=${sessionId} reason=missing-env env=PLATFORM_PORT line=${JSON.stringify(line)}`);
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: "session-retrospective", level: "info", line }),
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(`[session-retrospective] log-ingest-failed sessionId=${sessionId} err=${err instanceof Error ? err.message : String(err)}`);
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: `Retrospective marked complete (learnings=${learningsCount} preferences=${preferencesCount} graphWrites=${graphWriteCount} typedEdges=${typedEdgesCount} nodesProcessed=${nodesProcessed}). Session is now free to end.`,
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
  });