@rubytech/create-maxy 1.0.421 → 1.0.423
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/maxy/server.js +194 -9
- package/payload/platform/templates/agents/admin/IDENTITY.md +7 -2
- package/payload/premium-plugins/real-agency/plugins/real-agency-coaching/PLUGIN.md +21 -0
- package/payload/premium-plugins/real-agency/plugins/real-agency-coaching/skills/agent-performance/SKILL.md +5 -1
- package/payload/premium-plugins/real-agency/plugins/real-agency-coaching/skills/bespoke-coaching/SKILL.md +8 -0
- package/payload/premium-plugins/real-agency/plugins/real-agency-coaching/skills/coaching-toolkit/SKILL.md +3 -1
- package/payload/premium-plugins/real-agency/plugins/real-agency-coaching/skills/serhant-training/SKILL.md +1 -1
package/package.json
CHANGED
package/payload/maxy/server.js
CHANGED
|
@@ -3822,7 +3822,7 @@ async function loadSessionContext(accountId) {
|
|
|
3822
3822
|
try {
|
|
3823
3823
|
const summaryResult = await session.run(
|
|
3824
3824
|
`MATCH (n:CreativeWork {accountId: $accountId})
|
|
3825
|
-
WHERE n.title STARTS WITH 'Session Summary'
|
|
3825
|
+
WHERE n.title STARTS WITH 'Session Summary' OR n.title = 'Session Snapshot'
|
|
3826
3826
|
RETURN n.title AS title, n.createdAt AS createdAt, n.abstract AS abstract
|
|
3827
3827
|
ORDER BY n.createdAt DESC LIMIT 1`,
|
|
3828
3828
|
{ accountId }
|
|
@@ -3852,14 +3852,24 @@ async function loadSessionContext(accountId) {
|
|
|
3852
3852
|
{ accountId, limit: neo4j.int(MAX_SESSION_TASKS) }
|
|
3853
3853
|
);
|
|
3854
3854
|
const sections = [];
|
|
3855
|
+
const STALENESS_LIMIT_MS = 48 * 60 * 60 * 1e3;
|
|
3855
3856
|
if (summaryResult.records.length > 0) {
|
|
3856
3857
|
const rec = summaryResult.records[0];
|
|
3857
3858
|
const title = rec.get("title");
|
|
3858
3859
|
const createdAt = rec.get("createdAt");
|
|
3859
3860
|
const abstract = rec.get("abstract");
|
|
3860
|
-
|
|
3861
|
-
|
|
3861
|
+
let isStale = false;
|
|
3862
|
+
if (createdAt) {
|
|
3863
|
+
const ageMs = Date.now() - new Date(createdAt).getTime();
|
|
3864
|
+
isStale = !isNaN(ageMs) && ageMs > STALENESS_LIMIT_MS;
|
|
3865
|
+
}
|
|
3866
|
+
if (isStale) {
|
|
3867
|
+
console.error(`[session-context] Skipped stale summary for ${accountId.slice(0, 8)}\u2026: title="${title}" createdAt=${createdAt}`);
|
|
3868
|
+
} else {
|
|
3869
|
+
const dateSuffix = createdAt ? ` (${createdAt.slice(0, 10)})` : "";
|
|
3870
|
+
sections.push(`## Last Session${dateSuffix}
|
|
3862
3871
|
${abstract}`);
|
|
3872
|
+
}
|
|
3863
3873
|
}
|
|
3864
3874
|
if (digestResult.records.length > 0) {
|
|
3865
3875
|
const rec = digestResult.records[0];
|
|
@@ -3885,8 +3895,9 @@ ${abstract}`);
|
|
|
3885
3895
|
${taskLines.join("\n")}`);
|
|
3886
3896
|
}
|
|
3887
3897
|
if (sections.length === 0) return null;
|
|
3898
|
+
const summaryCreatedAt = summaryResult.records.length > 0 ? summaryResult.records[0].get("createdAt") ?? "unknown" : "n/a";
|
|
3888
3899
|
console.error(
|
|
3889
|
-
`[session-context] Loaded for ${accountId.slice(0, 8)}\u2026: summary=${summaryResult.records.length > 0 ? "yes" : "no"}, digest=${digestResult.records.length > 0 ? "yes" : "no"}, tasks=${tasksResult.records.length}`
|
|
3900
|
+
`[session-context] Loaded for ${accountId.slice(0, 8)}\u2026: summary=${summaryResult.records.length > 0 ? "yes" : "no"}, summaryDate=${summaryCreatedAt}, digest=${digestResult.records.length > 0 ? "yes" : "no"}, tasks=${tasksResult.records.length}`
|
|
3890
3901
|
);
|
|
3891
3902
|
return `<previous-context>
|
|
3892
3903
|
${sections.join("\n\n")}
|
|
@@ -3898,6 +3909,22 @@ ${sections.join("\n\n")}
|
|
|
3898
3909
|
await session.close();
|
|
3899
3910
|
}
|
|
3900
3911
|
}
|
|
3912
|
+
async function writeSessionSummary(accountId, summary) {
|
|
3913
|
+
const session = getSession();
|
|
3914
|
+
try {
|
|
3915
|
+
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3916
|
+
await session.run(
|
|
3917
|
+
`MERGE (n:CreativeWork {accountId: $accountId, title: 'Session Snapshot'})
|
|
3918
|
+
SET n.abstract = $summary, n.createdAt = $createdAt, n.scope = 'admin'`,
|
|
3919
|
+
{ accountId, summary, createdAt }
|
|
3920
|
+
);
|
|
3921
|
+
console.error(`[session-summary] Written for ${accountId.slice(0, 8)}\u2026`);
|
|
3922
|
+
} catch (err) {
|
|
3923
|
+
console.error(`[session-summary] Failed for ${accountId.slice(0, 8)}\u2026: ${err instanceof Error ? err.message : String(err)}`);
|
|
3924
|
+
} finally {
|
|
3925
|
+
await session.close();
|
|
3926
|
+
}
|
|
3927
|
+
}
|
|
3901
3928
|
async function loadOnboardingStep(accountId) {
|
|
3902
3929
|
const session = getSession();
|
|
3903
3930
|
try {
|
|
@@ -4777,7 +4804,16 @@ function validateSession(sessionKey, agentType) {
|
|
|
4777
4804
|
if (!session) return false;
|
|
4778
4805
|
if (session.agentType !== agentType) return false;
|
|
4779
4806
|
if (Date.now() - session.createdAt > 24 * 60 * 60 * 1e3) {
|
|
4807
|
+
const history = session.messageHistory;
|
|
4808
|
+
const expiredAccountId = session.accountId;
|
|
4780
4809
|
sessionStore.delete(sessionKey);
|
|
4810
|
+
if (history && history.length >= 2) {
|
|
4811
|
+
writeEndOfTurnSummary(expiredAccountId, history).catch(
|
|
4812
|
+
(err) => console.error(`[session-summary] expiry write failed: ${err instanceof Error ? err.message : String(err)}`)
|
|
4813
|
+
);
|
|
4814
|
+
} else {
|
|
4815
|
+
console.error(`[session-summary] Session ${sessionKey.slice(0, 8)}\u2026 expired without summary \u2014 ${history?.length ?? 0} messages`);
|
|
4816
|
+
}
|
|
4781
4817
|
return false;
|
|
4782
4818
|
}
|
|
4783
4819
|
if (session.grantExpiresAt && Date.now() > session.grantExpiresAt) {
|
|
@@ -5061,6 +5097,80 @@ function getAdminAllowedTools(enabledPlugins) {
|
|
|
5061
5097
|
}
|
|
5062
5098
|
return tools;
|
|
5063
5099
|
}
|
|
5100
|
+
var QUERY_CLASSIFIER_MODEL = "claude-haiku-4-5-20251001";
|
|
5101
|
+
var QUERY_CLASSIFIER_TIMEOUT_MS = 3e3;
|
|
5102
|
+
var QUERY_CLASSIFIER_MSG_CAP = 500;
|
|
5103
|
+
var QUERY_CLASSIFIER_HISTORY_TURNS = 4;
|
|
5104
|
+
var QUERY_CLASSIFIER_FALLBACK = {
|
|
5105
|
+
search: true,
|
|
5106
|
+
query: null,
|
|
5107
|
+
// null means "use raw message"
|
|
5108
|
+
reason: "classifier-fallback"
|
|
5109
|
+
};
|
|
5110
|
+
var QUERY_CLASSIFIER_SYSTEM = `You decide whether a user message to a business assistant requires a knowledge base search.
|
|
5111
|
+
|
|
5112
|
+
The knowledge base contains the business's products, services, pricing, policies, and domain expertise. It does NOT contain conversation history \u2014 the assistant already has that.
|
|
5113
|
+
|
|
5114
|
+
Respond with ONLY a JSON object: {"search": boolean, "query": string | null, "reason": string}
|
|
5115
|
+
|
|
5116
|
+
Set search to false when:
|
|
5117
|
+
- The message is a greeting, farewell, acknowledgement, or filler ("hello", "thanks", "ok", "bye")
|
|
5118
|
+
- The message is about session state ("continue where we left off", "what were we discussing")
|
|
5119
|
+
- The conversation history already contains sufficient information to answer the question
|
|
5120
|
+
- The message is a conversational response that doesn't require new factual knowledge
|
|
5121
|
+
|
|
5122
|
+
Set search to true when:
|
|
5123
|
+
- The message asks about products, services, pricing, capabilities, or business information
|
|
5124
|
+
- The message introduces a new topic not already covered in the conversation
|
|
5125
|
+
- The message requires factual knowledge the assistant wouldn't have from conversation alone
|
|
5126
|
+
|
|
5127
|
+
When search is true, set query to a search-optimised phrase that captures the information need. Resolve references from conversation history \u2014 e.g. if the user says "tell me more about the first one" and the history mentions "property valuations", set query to "property valuations details". If the raw message is already a good search query, use it as-is.
|
|
5128
|
+
|
|
5129
|
+
When search is false, set query to null and reason to a brief category (e.g. "greeting", "acknowledgement", "session-reference", "context-sufficient").`;
|
|
5130
|
+
async function classifyMemoryQuery(message, history) {
|
|
5131
|
+
let apiKey = process.env.ANTHROPIC_API_KEY;
|
|
5132
|
+
if (!apiKey) {
|
|
5133
|
+
try {
|
|
5134
|
+
apiKey = readFileSync5(API_KEY_FILE, "utf-8").trim();
|
|
5135
|
+
} catch {
|
|
5136
|
+
}
|
|
5137
|
+
}
|
|
5138
|
+
if (!apiKey) return QUERY_CLASSIFIER_FALLBACK;
|
|
5139
|
+
const recentHistory = history.slice(-QUERY_CLASSIFIER_HISTORY_TURNS);
|
|
5140
|
+
const transcript = recentHistory.length > 0 ? recentHistory.map((m) => `[${m.role}] ${m.content.slice(0, QUERY_CLASSIFIER_MSG_CAP)}`).join("\n") : "(no conversation history \u2014 this is the first message)";
|
|
5141
|
+
const userContent = [
|
|
5142
|
+
`Conversation history:
|
|
5143
|
+
${transcript}`,
|
|
5144
|
+
`
|
|
5145
|
+
Current message:
|
|
5146
|
+
${message.slice(0, QUERY_CLASSIFIER_MSG_CAP)}`
|
|
5147
|
+
].join("\n");
|
|
5148
|
+
const controller = new AbortController();
|
|
5149
|
+
const timer = setTimeout(() => controller.abort(), QUERY_CLASSIFIER_TIMEOUT_MS);
|
|
5150
|
+
try {
|
|
5151
|
+
const client = new Anthropic({ apiKey });
|
|
5152
|
+
const response = await client.messages.create({
|
|
5153
|
+
model: QUERY_CLASSIFIER_MODEL,
|
|
5154
|
+
max_tokens: 150,
|
|
5155
|
+
system: QUERY_CLASSIFIER_SYSTEM,
|
|
5156
|
+
messages: [{ role: "user", content: userContent }]
|
|
5157
|
+
}, { signal: controller.signal });
|
|
5158
|
+
const text = response.content.filter((b) => b.type === "text").map((b) => "text" in b ? b.text : "").join("").trim();
|
|
5159
|
+
if (!text) return QUERY_CLASSIFIER_FALLBACK;
|
|
5160
|
+
const jsonStr = text.replace(/^```json\s*/i, "").replace(/```\s*$/, "").trim();
|
|
5161
|
+
const parsed = JSON.parse(jsonStr);
|
|
5162
|
+
if (typeof parsed.search !== "boolean") return QUERY_CLASSIFIER_FALLBACK;
|
|
5163
|
+
return {
|
|
5164
|
+
search: parsed.search,
|
|
5165
|
+
query: parsed.search && typeof parsed.query === "string" ? parsed.query.slice(0, QUERY_CLASSIFIER_MSG_CAP) : null,
|
|
5166
|
+
reason: typeof parsed.reason === "string" ? parsed.reason.replace(/[\n\r]/g, " ").slice(0, 100) : "unknown"
|
|
5167
|
+
};
|
|
5168
|
+
} catch {
|
|
5169
|
+
return QUERY_CLASSIFIER_FALLBACK;
|
|
5170
|
+
} finally {
|
|
5171
|
+
clearTimeout(timer);
|
|
5172
|
+
}
|
|
5173
|
+
}
|
|
5064
5174
|
async function fetchMemoryContext(accountId, query, sessionKey, options) {
|
|
5065
5175
|
const serverPath = resolve4(PLATFORM_ROOT3, "plugins/memory/mcp/dist/index.js");
|
|
5066
5176
|
if (!existsSync5(serverPath)) {
|
|
@@ -5456,6 +5566,65 @@ Extract preference updates as JSON array.`
|
|
|
5456
5566
|
clearTimeout(timer);
|
|
5457
5567
|
}
|
|
5458
5568
|
}
|
|
5569
|
+
var SESSION_SUMMARY_PROMPT = `You are summarising a conversation between a business assistant and its owner. Write a 3-5 sentence summary covering:
|
|
5570
|
+
- What was discussed and accomplished this session
|
|
5571
|
+
- Any tasks created, decisions made, or state changes
|
|
5572
|
+
- Current state of any ongoing work
|
|
5573
|
+
- What the owner is likely to want to continue next
|
|
5574
|
+
|
|
5575
|
+
Be factual and specific. Use present tense for current state. Do not include greetings or meta-commentary.`;
|
|
5576
|
+
var SESSION_SUMMARY_TIMEOUT_MS = 1e4;
|
|
5577
|
+
var lastSummaryWrite = /* @__PURE__ */ new Map();
|
|
5578
|
+
var SUMMARY_RATE_LIMIT_MS = 5 * 60 * 1e3;
|
|
5579
|
+
async function generateSessionSummary(history) {
|
|
5580
|
+
if (history.length < 2) return null;
|
|
5581
|
+
let apiKey = process.env.ANTHROPIC_API_KEY;
|
|
5582
|
+
if (!apiKey) {
|
|
5583
|
+
try {
|
|
5584
|
+
apiKey = readFileSync5(API_KEY_FILE, "utf-8").trim();
|
|
5585
|
+
} catch {
|
|
5586
|
+
return null;
|
|
5587
|
+
}
|
|
5588
|
+
}
|
|
5589
|
+
if (!apiKey) {
|
|
5590
|
+
console.error("[session-summary] Skipping \u2014 no API key available");
|
|
5591
|
+
return null;
|
|
5592
|
+
}
|
|
5593
|
+
const transcript = history.slice(-20).map((m) => `[${m.role}] ${m.content}`).join("\n\n");
|
|
5594
|
+
const controller = new AbortController();
|
|
5595
|
+
const timer = setTimeout(() => controller.abort(), SESSION_SUMMARY_TIMEOUT_MS);
|
|
5596
|
+
try {
|
|
5597
|
+
const client = new Anthropic({ apiKey });
|
|
5598
|
+
const response = await client.messages.create({
|
|
5599
|
+
model: REFLECTION_MODEL,
|
|
5600
|
+
max_tokens: 500,
|
|
5601
|
+
system: SESSION_SUMMARY_PROMPT,
|
|
5602
|
+
messages: [{ role: "user", content: `Conversation:
|
|
5603
|
+
${transcript}` }]
|
|
5604
|
+
}, { signal: controller.signal });
|
|
5605
|
+
return response.content.filter((b) => b.type === "text").map((b) => "text" in b ? b.text : "").join("");
|
|
5606
|
+
} catch (err) {
|
|
5607
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
5608
|
+
console.error(`[session-summary] Haiku call timed out after ${SESSION_SUMMARY_TIMEOUT_MS}ms`);
|
|
5609
|
+
} else {
|
|
5610
|
+
console.error(`[session-summary] Haiku call failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
5611
|
+
}
|
|
5612
|
+
return null;
|
|
5613
|
+
} finally {
|
|
5614
|
+
clearTimeout(timer);
|
|
5615
|
+
}
|
|
5616
|
+
}
|
|
5617
|
+
async function writeEndOfTurnSummary(accountId, history) {
|
|
5618
|
+
const now = Date.now();
|
|
5619
|
+
const lastWrite = lastSummaryWrite.get(accountId) ?? 0;
|
|
5620
|
+
if (now - lastWrite < SUMMARY_RATE_LIMIT_MS) {
|
|
5621
|
+
return;
|
|
5622
|
+
}
|
|
5623
|
+
const summary = await generateSessionSummary(history);
|
|
5624
|
+
if (!summary) return;
|
|
5625
|
+
await writeSessionSummary(accountId, summary);
|
|
5626
|
+
lastSummaryWrite.set(accountId, Date.now());
|
|
5627
|
+
}
|
|
5459
5628
|
var MANAGED_WORK_BUDGET_RATIO = 0.5;
|
|
5460
5629
|
var TOOL_SCHEMA_OVERHEAD_TOKENS = 15e3;
|
|
5461
5630
|
function estimateTokens(text) {
|
|
@@ -6503,6 +6672,9 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
|
|
|
6503
6672
|
if (responseText) persistMessage(convId, "assistant", responseText, accountId, capturedTokens, assistantTimestamp).catch(() => {
|
|
6504
6673
|
});
|
|
6505
6674
|
}
|
|
6675
|
+
writeEndOfTurnSummary(accountId, afterHistory).catch(
|
|
6676
|
+
(err) => console.error(`[session-summary] end-of-turn write failed: ${err instanceof Error ? err.message : String(err)}`)
|
|
6677
|
+
);
|
|
6506
6678
|
}
|
|
6507
6679
|
yield event;
|
|
6508
6680
|
}
|
|
@@ -6581,18 +6753,32 @@ async function* invokePublicAgent(message, systemPrompt, accountId, accountDir,
|
|
|
6581
6753
|
}
|
|
6582
6754
|
}
|
|
6583
6755
|
}
|
|
6756
|
+
const history = sessionKey ? getMessageHistory(sessionKey) : [];
|
|
6584
6757
|
const fetchOptions = {
|
|
6585
6758
|
...agentSlug ? { agentSlug } : {},
|
|
6586
6759
|
...knowledgeKeywords && knowledgeKeywords.length > 0 ? { knowledgeKeywords } : {}
|
|
6587
6760
|
};
|
|
6761
|
+
const needsClassification = !(knowledgeBaked && !liveMemory);
|
|
6762
|
+
const classifyStartMs = Date.now();
|
|
6763
|
+
const classification = needsClassification ? await classifyMemoryQuery(message, history) : QUERY_CLASSIFIER_FALLBACK;
|
|
6764
|
+
const classifyMs = Date.now() - classifyStartMs;
|
|
6765
|
+
const effectiveQuery = classification.query ?? message;
|
|
6766
|
+
if (needsClassification) {
|
|
6767
|
+
streamLog.write(`[${isoTs()}] [public-query-classifier] search=${classification.search} effectiveQuery=${JSON.stringify(effectiveQuery.slice(0, 200))} reason=${classification.reason} latencyMs=${classifyMs}
|
|
6768
|
+
`);
|
|
6769
|
+
}
|
|
6588
6770
|
let system;
|
|
6589
6771
|
if (knowledgeBaked && !liveMemory) {
|
|
6590
6772
|
streamLog.write(`[${isoTs()}] [public-knowledge-source] baked \u2014 skipping memory-search (liveMemory=false)
|
|
6773
|
+
`);
|
|
6774
|
+
system = systemPrompt;
|
|
6775
|
+
} else if (!classification.search) {
|
|
6776
|
+
streamLog.write(`[${isoTs()}] [public-knowledge-source] skipped \u2014 classifier (reason=${classification.reason})
|
|
6591
6777
|
`);
|
|
6592
6778
|
system = systemPrompt;
|
|
6593
6779
|
} else if (knowledgeBaked && liveMemory) {
|
|
6594
|
-
const memoryContext = await fetchMemoryContext(accountId,
|
|
6595
|
-
streamLog.write(`[${isoTs()}] [public-knowledge-source] baked+live agentSlug=${agentSlug ?? "none"} keywords=${knowledgeKeywords?.length ?? 0} result=${memoryContext ? `ok (${memoryContext.length} chars)` : "null"}
|
|
6780
|
+
const memoryContext = await fetchMemoryContext(accountId, effectiveQuery, sessionKey, fetchOptions);
|
|
6781
|
+
streamLog.write(`[${isoTs()}] [public-knowledge-source] baked+live agentSlug=${agentSlug ?? "none"} keywords=${knowledgeKeywords?.length ?? 0} effectiveQuery=${JSON.stringify(effectiveQuery.slice(0, 200))} result=${memoryContext ? `ok (${memoryContext.length} chars)` : "null"}
|
|
6596
6782
|
`);
|
|
6597
6783
|
system = memoryContext ? `${systemPrompt}
|
|
6598
6784
|
|
|
@@ -6600,8 +6786,8 @@ async function* invokePublicAgent(message, systemPrompt, accountId, accountDir,
|
|
|
6600
6786
|
${memoryContext}
|
|
6601
6787
|
</live-memory>` : systemPrompt;
|
|
6602
6788
|
} else {
|
|
6603
|
-
const memoryContext = await fetchMemoryContext(accountId,
|
|
6604
|
-
streamLog.write(`[${isoTs()}] [public-knowledge-source] memory-search agentSlug=${agentSlug ?? "none"} keywords=${knowledgeKeywords?.length ?? 0} result=${memoryContext ? `ok (${memoryContext.length} chars)` : "null \u2014 agent has no knowledge"}
|
|
6789
|
+
const memoryContext = await fetchMemoryContext(accountId, effectiveQuery, sessionKey, fetchOptions);
|
|
6790
|
+
streamLog.write(`[${isoTs()}] [public-knowledge-source] memory-search agentSlug=${agentSlug ?? "none"} keywords=${knowledgeKeywords?.length ?? 0} effectiveQuery=${JSON.stringify(effectiveQuery.slice(0, 200))} result=${memoryContext ? `ok (${memoryContext.length} chars)` : "null \u2014 agent has no knowledge"}
|
|
6605
6791
|
`);
|
|
6606
6792
|
if (memoryContext) {
|
|
6607
6793
|
streamLog.write(`[${isoTs()}] [public-memory-context] ${JSON.stringify(memoryContext.slice(0, 500))}${memoryContext.length > 500 ? "\u2026" : ""}
|
|
@@ -6613,7 +6799,6 @@ ${memoryContext}
|
|
|
6613
6799
|
${memoryContext}
|
|
6614
6800
|
</memory>` : systemPrompt;
|
|
6615
6801
|
}
|
|
6616
|
-
const history = sessionKey ? getMessageHistory(sessionKey) : [];
|
|
6617
6802
|
const sdkMessages = [
|
|
6618
6803
|
...history.map((m) => ({ role: m.role, content: m.content })),
|
|
6619
6804
|
{ role: "user", content: message }
|
|
@@ -87,9 +87,14 @@ For the most recent session: call `session-list` with `limit: 1`, then call `ses
|
|
|
87
87
|
|
|
88
88
|
## Session Memory
|
|
89
89
|
|
|
90
|
-
The platform automatically injects recalled context into your system prompt as a `<previous-context>` section. This includes the most recent session summary and all open/active tasks.
|
|
90
|
+
The platform automatically injects recalled context into your system prompt as a `<previous-context>` section. This includes the most recent session summary and all open/active tasks. The platform rejects stale summaries (older than 48 hours) — when present, the summary is recent and trustworthy.
|
|
91
91
|
|
|
92
|
-
When `<previous-context>` is present
|
|
92
|
+
When `<previous-context>` is present:
|
|
93
|
+
- **Trust the session summary for state orientation.** It reflects the end of the most recent session. Do not re-verify claims already described — if the summary says onboarding is complete, do not call `onboarding-get`. If it names tasks in progress, acknowledge and resume them.
|
|
94
|
+
- **Do not re-research context captured in the summary.** When the summary describes work in progress, outstanding tasks, or recent decisions, pick up from that state. Redundant memory-search or tool calls for information already in the summary waste the owner's time.
|
|
95
|
+
- Use it to greet the owner with awareness of prior work and outstanding tasks.
|
|
96
|
+
|
|
97
|
+
When `<previous-context>` is absent, Neo4j was unreachable or no prior context exists — proceed normally, using tool calls to establish state.
|
|
93
98
|
|
|
94
99
|
In managed context mode, conversation history is provided within `<conversation-history>` tags. Use `session-compact-status` to retrieve older archived context if needed.
|
|
95
100
|
|
|
@@ -23,6 +23,27 @@ The user is coaching team members, reviewing performance, setting goals, running
|
|
|
23
23
|
| `agent-performance` | HP6 model, 12 Week Year, Atomic Habits, daily routines and scorecards |
|
|
24
24
|
| `serhant-training` | Serhant's complete sales training methodology adapted for UK estate agency |
|
|
25
25
|
|
|
26
|
+
## Coach vs Mentor
|
|
27
|
+
|
|
28
|
+
Real Agent is the **daily coach** — present every day, seeing every rep, providing operational accountability. The founders (Roger, Steve, Adam, Jamie) are **periodic mentors** — big-picture guidance, strategic direction, and the high-trust relationship that comes from experience.
|
|
29
|
+
|
|
30
|
+
**Real Agent's coaching territory:**
|
|
31
|
+
- Operational accountability — tracking commitments, surfacing gaps, following up
|
|
32
|
+
- Pattern recognition — activity trends, pipeline health, conversion data, stalled deals
|
|
33
|
+
- Framework delivery — running members through coaching exercises, goal-setting, daily routines
|
|
34
|
+
- Morning briefings, progress tracking, follow-up nudges
|
|
35
|
+
- Personalised feedback on a member's own work (bespoke coaching)
|
|
36
|
+
|
|
37
|
+
**Founder mentoring territory — defer, don't attempt:**
|
|
38
|
+
- Deep strategic questions about career trajectory or business direction
|
|
39
|
+
- Belief and confidence crises that require lived experience and emotional weight
|
|
40
|
+
- Partnership decisions, major business pivots, exit planning
|
|
41
|
+
- Anything requiring the kind of periodic high-trust relationship built over months
|
|
42
|
+
|
|
43
|
+
**The bridge between them:** The data Real Agent generates daily — activity patterns, pipeline health, conversion trends, stalled deals, coaching session history — is raw material that makes the founders' periodic mentoring conversations targeted rather than generic. Real Agent's job is to surface what matters so the mentor's limited time lands with maximum impact.
|
|
44
|
+
|
|
45
|
+
Each skill inherits this boundary. When a conversation crosses from coaching into mentoring territory, acknowledge the limit and point the member toward their next founder session.
|
|
46
|
+
|
|
26
47
|
## Tools Used
|
|
27
48
|
|
|
28
49
|
No MCP server. Skills operate via existing platform tools:
|
|
@@ -5,7 +5,11 @@ description: "High-performance frameworks, productivity systems, and daily execu
|
|
|
5
5
|
|
|
6
6
|
# Agent Performance Skill
|
|
7
7
|
|
|
8
|
-
You are
|
|
8
|
+
You are the daily performance coach for estate agency professionals — helping negotiators, valuers, listers, and team leaders build elite daily habits, execute with discipline, and continuously improve their results. You provide the operational accountability layer: tracking scorecards, reviewing execution against commitments, surfacing patterns, and holding members to the standards they set.
|
|
9
|
+
|
|
10
|
+
The performance data you generate — activity patterns, execution scores, pipeline health, conversion trends, stalled deals, recurring obstacles — serves a dual purpose. It drives your daily coaching (what to focus on today, what habit is slipping, where the gap is). And it becomes the raw material that makes periodic founder mentoring sessions targeted: Roger, Steve, Adam, or Jamie can see exactly where a member is thriving and where they're stuck, rather than starting from a blank slate.
|
|
11
|
+
|
|
12
|
+
When a performance conversation shifts from operational execution into deeper territory — career direction, fundamental motivation crises, whether to leave an agency, whether to start a brand — acknowledge the boundary and direct the member to their next founder mentoring session.
|
|
9
13
|
|
|
10
14
|
Use UK estate agency terminology throughout (valuation not appraisal, instruction not listing, vendor not seller, negotiator not agent, estate agent not realtor, completions not closings, exchanges not settlements).
|
|
11
15
|
|
|
@@ -18,6 +18,14 @@ A member shares their own work for feedback: a listing presentation, social medi
|
|
|
18
18
|
| Feedback framework | Member shares work for review | `references/feedback-framework.md` |
|
|
19
19
|
| Coaching boundaries | Edge cases, limitations, escalation | `references/coaching-boundaries.md` |
|
|
20
20
|
|
|
21
|
+
## Your Role: Daily Coach
|
|
22
|
+
|
|
23
|
+
This skill is Real Agent's daily coaching layer — operational accountability through personalised feedback on a member's own work. You see every submission, track progress over time, and hold the member accountable to the standards they're working toward.
|
|
24
|
+
|
|
25
|
+
**What you do:** Provide honest, expert-grounded feedback on specific work products. Surface patterns across submissions. Push for improvement. Celebrate progress.
|
|
26
|
+
|
|
27
|
+
**What you don't do:** Answer deep strategic questions about career trajectory, business direction, or major life decisions. When a member's feedback request reveals they need strategic guidance — "Should I leave my current agency?", "Am I ready to start my own brand?", "I've lost confidence in this career" — acknowledge the limit. Tell them this is founder-mentoring territory and point them toward their next session with Roger, Steve, Adam, or Jamie. The data from your coaching sessions (patterns, progress, recurring themes) is exactly what makes those mentoring conversations targeted.
|
|
28
|
+
|
|
21
29
|
## Key Rules
|
|
22
30
|
|
|
23
31
|
- Ground all feedback in contributor expertise. "Adam's approach would be..." not "best practice suggests..."
|
|
@@ -5,7 +5,9 @@ description: "Life and career coaching frameworks for estate agency managers, te
|
|
|
5
5
|
|
|
6
6
|
# Coaching Toolkit Skill
|
|
7
7
|
|
|
8
|
-
You are
|
|
8
|
+
You are the daily coaching layer for estate agency leaders and managers — facilitating 1-on-1s, coaching sessions, team development conversations, and goal-setting workshops using proven coaching frameworks. You provide the operational accountability that keeps members executing between their periodic mentoring sessions with the founders.
|
|
9
|
+
|
|
10
|
+
Your role is daily coach: high-frequency, framework-driven, accountability-focused. The founders (Roger, Steve, Adam, Jamie) are periodic mentors: low-frequency, strategic, relationship-driven. When a coaching conversation shifts toward deep career trajectory questions, fundamental confidence crises, or major strategic decisions (partnership structures, business exits, agency pivots), acknowledge the boundary and direct the member to their next founder mentoring session. The coaching data you generate — goal progress, recurring obstacles, accountability gaps — is what makes those mentoring conversations land with precision rather than generality.
|
|
9
11
|
|
|
10
12
|
Use UK estate agency terminology throughout (valuation not appraisal, instruction not listing, vendor not seller, negotiator not agent, estate agent not realtor, completions not closings, exchanges not settlements).
|
|
11
13
|
|
|
@@ -5,7 +5,7 @@ description: "Ryan Serhant's complete sales training methodology adapted for UK
|
|
|
5
5
|
|
|
6
6
|
# Serhant Training — UK Estate Agency Edition
|
|
7
7
|
|
|
8
|
-
This skill encodes Ryan Serhant's complete training methodology, adapted for the UK property market. Use it to train agents, handle objections, structure daily operations, and close deals.
|
|
8
|
+
This skill encodes Ryan Serhant's complete training methodology, adapted for the UK property market. Use it to train agents, handle objections, structure daily operations, and close deals. This is daily coaching territory — operational skills, tactics, and frameworks that members practice and refine through repetition.
|
|
9
9
|
|
|
10
10
|
> **UK Terminology:** Throughout this skill, US terms are replaced: vendor (not seller), instruction (not listing), market appraisal (not listing appointment), estate agent (not realtor/broker), £ (not $), solicitor (not attorney), EPC/searches (not board package), Rightmove/Zoopla (not MLS/Streeteasy).
|
|
11
11
|
|