@rubytech/create-maxy-code 0.1.127 → 0.1.131
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/dist/index.js +10 -7
- package/package.json +1 -1
- package/payload/platform/lib/graph-write/dist/conversation-provenance.d.ts +5 -1
- package/payload/platform/lib/graph-write/dist/conversation-provenance.d.ts.map +1 -1
- package/payload/platform/lib/graph-write/dist/conversation-provenance.js +18 -11
- package/payload/platform/lib/graph-write/dist/conversation-provenance.js.map +1 -1
- package/payload/platform/lib/graph-write/src/conversation-provenance.ts +24 -16
- package/payload/platform/plugins/contacts/PLUGIN.md +1 -1
- package/payload/platform/plugins/docs/references/internals.md +1 -1
- package/payload/platform/plugins/docs/references/memory-guide.md +4 -0
- package/payload/platform/plugins/graph-viewer/mcp/dist/index.js +2 -6
- package/payload/platform/plugins/graph-viewer/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/graph-viewer/mcp/dist/render/layout.d.ts.map +1 -1
- package/payload/platform/plugins/graph-viewer/mcp/dist/render/layout.js +46 -6
- package/payload/platform/plugins/graph-viewer/mcp/dist/render/layout.js.map +1 -1
- package/payload/platform/plugins/graph-viewer/mcp/dist/tools/graph-render.d.ts +1 -1
- package/payload/platform/plugins/graph-viewer/mcp/dist/tools/graph-render.d.ts.map +1 -1
- package/payload/platform/plugins/graph-viewer/mcp/dist/tools/graph-render.js +28 -2
- package/payload/platform/plugins/graph-viewer/mcp/dist/tools/graph-render.js.map +1 -1
- package/payload/platform/plugins/graph-viewer/skills/render-graph/SKILL.md +6 -3
- package/payload/platform/plugins/memory/PLUGIN.md +1 -1
- package/payload/platform/plugins/memory/references/schema-estate-agent.md +3 -3
- 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 +16 -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 +11 -0
- 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 +11 -0
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
- package/payload/platform/templates/agents/admin/IDENTITY.md +12 -0
- package/payload/platform/templates/agents/public/IDENTITY.md +8 -2
- package/payload/platform/templates/specialists/agents/content-producer.md +4 -0
- package/payload/platform/templates/specialists/agents/database-operator.md +2 -0
- package/payload/platform/templates/specialists/agents/personal-assistant.md +2 -0
- package/payload/server/server.js +70 -11
|
@@ -14,9 +14,15 @@ Three rules govern every turn. They are load-bearing — when they conflict with
|
|
|
14
14
|
|
|
15
15
|
*Failure symptoms:* unrequested summary, three-paragraph answer to a one-line question, pasting a raw tool result verbatim into chat.
|
|
16
16
|
|
|
17
|
-
**EVIDENCE-BASED.** The graph is the single, canonical source of truth about this account. Consult it — via `memory-search
|
|
17
|
+
**EVIDENCE-BASED.** The graph is the single, canonical source of truth about this account. Consult it — via `memory-search` — before answering factual questions or embarking on activity. Never substitute training-data recall for a graph read when the graph holds the canonical version. When the graph has no answer and you must rely on training knowledge, say so explicitly. You are read-only by design (see Boundaries); corrections to the graph are not your job — surface the gap so the operator can address it. *Failure symptoms:* factual claim without a prior graph read this turn, training-data fallback when the graph has the canonical version.
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
The 4-step brain-first loop, every turn that needs information:
|
|
20
|
+
1. **Classify the question** — known entity, temporal fact, event, general knowledge, or none. The inbound gateway provides this as `retrievalClass` when available.
|
|
21
|
+
2. **Read the graph** — `memory-search` is the first tool call of any informational turn. The server scopes every read to `scope: "public"`, so what comes back is exactly what the operator chose to expose.
|
|
22
|
+
3. **Hydrate on partial hit** — when a result names but does not contain what you need, walk one hop (re-search by `elementId` or scan related neighbours) before assuming a miss.
|
|
23
|
+
4. **Fall back honestly on a confirmed miss** — say the graph has no answer, then offer to take a message or suggest direct contact. Never invent the missing fact.
|
|
24
|
+
|
|
25
|
+
A landfill graph defeats EVIDENCE-BASED: search returns noise, the agent relays the noise, the visitor sees noise. Filter on read.
|
|
20
26
|
|
|
21
27
|
---
|
|
22
28
|
|
|
@@ -22,6 +22,10 @@ These three rules win when anything else in this prompt conflicts with them.
|
|
|
22
22
|
|
|
23
23
|
Producing reads from the graph; it never writes external input to it. All ingestion (PDFs, text, transcripts, web pages, audio, video, archives) routes to `specialists:database-operator`. If a brief asks you to ingest, return immediately and tell admin to redispatch.
|
|
24
24
|
|
|
25
|
+
## Brain-first read before produce
|
|
26
|
+
|
|
27
|
+
Every artifact you generate is grounded in graph content the operator already curated. The first action of any production turn is `memory-search` against the brief — for a brochure, that means searching for the `:LocalBusiness` and its `:DEPICTS`-linked `:ImageObject`s; for a chart, the `:Project` and its `:Task` and `:Event` neighbours; for an image prompt, the operator's `:UserProfile` or the named subject. Compose the artifact from properties the search returned. Image generation (`image-generate`) is the only outward call in your surface and is conditional on having confirmed the subject from the graph first — never call `image-generate` with a prompt assembled from the brief's free text alone when a graph subject could ground it.
|
|
28
|
+
|
|
25
29
|
## Image generation
|
|
26
30
|
|
|
27
31
|
Three models via `image-generate`. Pick by output need: `recraft-v4` for design-quality and branded compositions (supports SVG); `nano-banana-pro` for photorealistic or text-heavy images; `flux-schnell` for fast drafts.
|
|
@@ -16,4 +16,6 @@ Node `name` and `title` properties are short identifiers, never denormalised com
|
|
|
16
16
|
|
|
17
17
|
The `accountId` property is supplied by the writers from server-side environment state (the `ACCOUNT_ID` env var) — never refuse a write because accountId is absent from admin's brief.
|
|
18
18
|
|
|
19
|
+
**Brain-first read before write.** Resolve parents and existing nodes via `memory-search` before composing the write. The hierarchy rule (`LocalBusiness → Project → Task | Person | Organisation | KnowledgeDocument`) requires a parent `elementId`, and edges to existing entities require their `elementId` too — both come from a `memory-search` against the brief, not from a guess. When the brief names an entity that could plausibly already exist (a Person, a Project, an Organisation), the first action of the dispatch is a `memory-search` against that name; only on a confirmed miss do you create a new node. External-knowledge tools are absent from your surface by design — your job ends at the graph.
|
|
20
|
+
|
|
19
21
|
Decide the precise operations against the schema, execute them with the writers in your tool surface, and report each write back to admin as `{label, elementId}` so admin can confirm the outcome to the operator. If admin's brief is ambiguous about which label, which edge type, or which existing node to attach to, name the ambiguity in your reply rather than guessing.
|
|
@@ -28,6 +28,8 @@ Each domain has a small set of tools and, where it exists, a skill that drives t
|
|
|
28
28
|
|
|
29
29
|
## Cross-domain rules
|
|
30
30
|
|
|
31
|
+
**Brain-first read before action.** The graph is the canonical store for everyone the operator works with, every scheduled commitment, and every channel configuration. Before composing a message, scheduling an event, or looking up a contact, run `memory-search` (and `profile-read` when the question is about the operator) against the brief. Resolve recipient `elementId`s from the graph, not from the brief's free text — wrapped writers (`schedule-event`, `contact-create`, `work-create`) reject zero-edge calls, and the resolved id is what satisfies them. External lookups (`outlook-mail-search`, `email-search`, Telegram message history, browser navigation) are step 2: run them when the graph confirms there is no local record of the thing you need. Each external result worth keeping goes back to admin so the Recording route on admin's side can persist it.
|
|
32
|
+
|
|
31
33
|
**Credentials never leave a tool.** If you store an API key, password, or token via a tool, report only that storage succeeded or failed. Never repeat the secret in your output, even partially.
|
|
32
34
|
|
|
33
35
|
**Timezones are not optional.** Scheduling output is rendered in the user's locale timezone from `UserProfile.timezone` (IANA). Storage is UTC. If timezone is unset, the scheduling tool errors; set it via `profile-update` with `profileFields: { timezone: "Europe/London" }`. There is no UTC fallback.
|
package/payload/server/server.js
CHANGED
|
@@ -4441,6 +4441,10 @@ function defaultResult(rawText, latencyMs) {
|
|
|
4441
4441
|
language: "en",
|
|
4442
4442
|
requiresHistory: false,
|
|
4443
4443
|
complexity: "complex",
|
|
4444
|
+
// Fallthrough defaults to "general" so the brain-first loop still runs a
|
|
4445
|
+
// graph read — safer than "none", which would skip retrieval entirely on
|
|
4446
|
+
// every Haiku failure.
|
|
4447
|
+
retrievalClass: "general",
|
|
4444
4448
|
latencyMs,
|
|
4445
4449
|
fallthrough: true
|
|
4446
4450
|
};
|
|
@@ -4476,7 +4480,14 @@ LANGUAGE \u2014 detect the language and return its ISO 639-1 code (e.g. "en", "e
|
|
|
4476
4480
|
|
|
4477
4481
|
REQUIRES HISTORY \u2014 set to true if the message references prior conversation context ("we discussed", "last time", "that thing", "the proposal", "earlier")
|
|
4478
4482
|
|
|
4479
|
-
COMPLEXITY \u2014 "simple" for greetings, yes/no answers, brief confirmations; "complex" for multi-part requests, detailed questions, nuanced instructions
|
|
4483
|
+
COMPLEXITY \u2014 "simple" for greetings, yes/no answers, brief confirmations; "complex" for multi-part requests, detailed questions, nuanced instructions
|
|
4484
|
+
|
|
4485
|
+
RETRIEVAL CLASS \u2014 classify what kind of graph read this message warrants (brain-first lookup loop). One of:
|
|
4486
|
+
- "entity": names or asks about a specific person, organisation, place, listing, project, document \u2014 anything the graph might already know about ("who is Adam Langley", "tell me about Acme Ltd", "what do we have on 5 Glan Honddu Close")
|
|
4487
|
+
- "temporal": references a time-bound fact \u2014 past events, schedule, what happened when ("what did we agree last Tuesday", "what's on today", "when did the contract land")
|
|
4488
|
+
- "event": references something a tool returned earlier in this or a prior session ("did the upload finish", "what did the search find", "the result from earlier")
|
|
4489
|
+
- "general": a question or task that needs information from the graph but doesn't fall into the three categories above (open-ended ask, broad topic, free-form question)
|
|
4490
|
+
- "none": greeting, meta-instruction, complaint, acknowledgement, or any message that does not warrant a graph lookup ("hi", "thanks", "stop doing that", "this is broken")`;
|
|
4480
4491
|
}
|
|
4481
4492
|
var GATEWAY_TOOL = {
|
|
4482
4493
|
name: "classify_message",
|
|
@@ -4522,6 +4533,11 @@ var GATEWAY_TOOL = {
|
|
|
4522
4533
|
type: "string",
|
|
4523
4534
|
enum: ["simple", "complex"],
|
|
4524
4535
|
description: "Message complexity level"
|
|
4536
|
+
},
|
|
4537
|
+
retrievalClass: {
|
|
4538
|
+
type: "string",
|
|
4539
|
+
enum: ["entity", "temporal", "event", "general", "none"],
|
|
4540
|
+
description: "Brain-first retrieval class. entity=named entity lookup, temporal=time-bound fact, event=prior tool result reference, general=other informational, none=greeting/meta/complaint with no graph lookup warranted"
|
|
4525
4541
|
}
|
|
4526
4542
|
},
|
|
4527
4543
|
required: [
|
|
@@ -4532,7 +4548,8 @@ var GATEWAY_TOOL = {
|
|
|
4532
4548
|
"intent",
|
|
4533
4549
|
"language",
|
|
4534
4550
|
"requiresHistory",
|
|
4535
|
-
"complexity"
|
|
4551
|
+
"complexity",
|
|
4552
|
+
"retrievalClass"
|
|
4536
4553
|
]
|
|
4537
4554
|
}
|
|
4538
4555
|
};
|
|
@@ -4603,6 +4620,7 @@ async function processInbound(rawText, channel) {
|
|
|
4603
4620
|
const requiresHistory = input.requiresHistory === true;
|
|
4604
4621
|
const complexity = input.complexity === "simple" ? "simple" : "complex";
|
|
4605
4622
|
const searchQuery = typeof input.searchQuery === "string" ? input.searchQuery : null;
|
|
4623
|
+
const retrievalClass = ["entity", "temporal", "event", "general", "none"].includes(input.retrievalClass) ? input.retrievalClass : "general";
|
|
4606
4624
|
const isAdmin = channel === "web-admin" || channel === "telegram-admin";
|
|
4607
4625
|
const finalText = isAdmin ? rawText.trim() : processedText;
|
|
4608
4626
|
const result = {
|
|
@@ -4613,11 +4631,12 @@ async function processInbound(rawText, channel) {
|
|
|
4613
4631
|
language,
|
|
4614
4632
|
requiresHistory,
|
|
4615
4633
|
complexity,
|
|
4634
|
+
retrievalClass,
|
|
4616
4635
|
latencyMs,
|
|
4617
4636
|
fallthrough: false
|
|
4618
4637
|
};
|
|
4619
4638
|
console.log(
|
|
4620
|
-
`${TAG14} channel=${channel} verdict=${verdict} promptInjection=${promptInjectionRisk} intent=${intent} language=${language} complexity=${complexity} requiresHistory=${requiresHistory} rewrite=${!isAdmin && processedText !== rawText.trim()} searchQuery=${searchQuery ? "yes" : "null"} latency_ms=${latencyMs}`
|
|
4639
|
+
`${TAG14} channel=${channel} verdict=${verdict} promptInjection=${promptInjectionRisk} intent=${intent} language=${language} complexity=${complexity} retrievalClass=${retrievalClass} requiresHistory=${requiresHistory} rewrite=${!isAdmin && processedText !== rawText.trim()} searchQuery=${searchQuery ? "yes" : "null"} latency_ms=${latencyMs}`
|
|
4621
4640
|
);
|
|
4622
4641
|
if (verdict !== "clean") {
|
|
4623
4642
|
console.warn(
|
|
@@ -5078,6 +5097,9 @@ ${lines}]`;
|
|
|
5078
5097
|
let gatewayResult = null;
|
|
5079
5098
|
if (!isGreeting) {
|
|
5080
5099
|
gatewayResult = await processInbound(message, "web-public");
|
|
5100
|
+
console.log(
|
|
5101
|
+
`[chat-route] session=${session_key.slice(0, 8)} retrievalClass=${gatewayResult.retrievalClass} intent=${gatewayResult.intent}`
|
|
5102
|
+
);
|
|
5081
5103
|
if (gatewayResult.screening.verdict === "discard") {
|
|
5082
5104
|
console.log(
|
|
5083
5105
|
`[chat-route] session=${session_key.slice(0, 8)} gateway=discard reason=${gatewayResult.screening.reason}`
|
|
@@ -6480,9 +6502,17 @@ function readUsersFile() {
|
|
|
6480
6502
|
if (!raw) return [];
|
|
6481
6503
|
return JSON.parse(raw);
|
|
6482
6504
|
}
|
|
6505
|
+
function claudeSpawnPath() {
|
|
6506
|
+
const base = process.env.PATH ?? "/usr/bin:/bin:/usr/sbin:/sbin";
|
|
6507
|
+
return `${base}:/opt/homebrew/bin:/usr/local/bin`;
|
|
6508
|
+
}
|
|
6483
6509
|
function checkAuthStatus() {
|
|
6484
6510
|
try {
|
|
6485
|
-
execFileSync2("claude", ["auth", "status"], {
|
|
6511
|
+
execFileSync2("claude", ["auth", "status"], {
|
|
6512
|
+
encoding: "utf-8",
|
|
6513
|
+
timeout: 5e3,
|
|
6514
|
+
env: { ...process.env, PATH: claudeSpawnPath() }
|
|
6515
|
+
});
|
|
6486
6516
|
return true;
|
|
6487
6517
|
} catch {
|
|
6488
6518
|
return false;
|
|
@@ -6537,22 +6567,34 @@ app4.post("/claude-auth", async (c) => {
|
|
|
6537
6567
|
return c.json({ launched: cdpReady });
|
|
6538
6568
|
}
|
|
6539
6569
|
ensureLogDir();
|
|
6570
|
+
writeFileSync6(logPath("claude-auth"), "");
|
|
6571
|
+
const claudeAuthLogFd = openSync(logPath("claude-auth"), "a");
|
|
6572
|
+
const onClaudeOutput = (chunk) => writeSync(claudeAuthLogFd, chunk);
|
|
6573
|
+
if (process.platform === "darwin") {
|
|
6574
|
+
vncLog("claude-auth", { action: "start", transport: "native", platform: "darwin" });
|
|
6575
|
+
const claudeProc2 = spawn("claude", ["auth", "login"], {
|
|
6576
|
+
env: { ...process.env, PATH: claudeSpawnPath() },
|
|
6577
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
6578
|
+
});
|
|
6579
|
+
claudeProc2.unref();
|
|
6580
|
+
claudeProc2.stdout?.on("data", onClaudeOutput);
|
|
6581
|
+
claudeProc2.stderr?.on("data", onClaudeOutput);
|
|
6582
|
+
claudeProc2.once("close", () => closeSync(claudeAuthLogFd));
|
|
6583
|
+
return c.json({ started: true, transport: "native" });
|
|
6584
|
+
}
|
|
6540
6585
|
if (transport === "vnc") {
|
|
6541
6586
|
const vncReady = await ensureVnc();
|
|
6542
6587
|
if (!vncReady) return c.json({ error: "VNC display failed to start" }, 500);
|
|
6543
6588
|
}
|
|
6544
6589
|
await ensureCdp(transport);
|
|
6545
|
-
writeFileSync6(logPath("claude-auth"), "");
|
|
6546
6590
|
const chromiumWrapper = writeChromiumWrapper();
|
|
6547
6591
|
const x11Env = buildX11Env(chromiumWrapper, transport);
|
|
6548
6592
|
vncLog("claude-auth", { action: "start", transport });
|
|
6549
|
-
const claudeAuthLogFd = openSync(logPath("claude-auth"), "a");
|
|
6550
6593
|
const claudeProc = spawn("claude", ["auth", "login"], {
|
|
6551
6594
|
env: x11Env,
|
|
6552
6595
|
stdio: ["ignore", "pipe", "pipe"]
|
|
6553
6596
|
});
|
|
6554
6597
|
claudeProc.unref();
|
|
6555
|
-
const onClaudeOutput = (chunk) => writeSync(claudeAuthLogFd, chunk);
|
|
6556
6598
|
claudeProc.stdout?.on("data", onClaudeOutput);
|
|
6557
6599
|
claudeProc.stderr?.on("data", onClaudeOutput);
|
|
6558
6600
|
claudeProc.once("close", () => closeSync(claudeAuthLogFd));
|
|
@@ -8537,7 +8579,12 @@ async function performSpawnWithInitialMessage(args) {
|
|
|
8537
8579
|
dormantPlugins,
|
|
8538
8580
|
activePlugins,
|
|
8539
8581
|
specialistDomains,
|
|
8540
|
-
tunnelUrl
|
|
8582
|
+
tunnelUrl,
|
|
8583
|
+
// Task 382 — thread the active conversation's UUID through to the
|
|
8584
|
+
// manager so the PTY env carries CONVERSATION_NODE_ID. Undefined is
|
|
8585
|
+
// legitimate (autonomous spawns); the manager's UUID gate filters
|
|
8586
|
+
// unshapely values.
|
|
8587
|
+
conversationNodeId: args.conversationNodeId
|
|
8541
8588
|
});
|
|
8542
8589
|
console.log(`${TAG18} forward-spawn-start managerBase=${managerBase2()} bytes=${upstreamPayload.length} hidden=${args.hidden} specialist=${args.specialist ?? "none"}`);
|
|
8543
8590
|
const forwardStart = Date.now();
|
|
@@ -8632,6 +8679,7 @@ app15.post("/", async (c) => {
|
|
|
8632
8679
|
const specialist = typeof body.specialist === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(body.specialist) ? body.specialist : void 0;
|
|
8633
8680
|
const model = typeof body.model === "string" && /^[A-Za-z0-9._-]{1,64}$/.test(body.model) ? body.model : void 0;
|
|
8634
8681
|
console.log(`${TAG18} spawn-request-in surface=${authSurface} accountId=${senderId.slice(0, 8)} userId=${userId ? userId.slice(0, 8) : "absent"} channel=${channel} permissionMode=${permissionMode ?? "default"} specialist=${specialist ?? "none"} model=${model ?? "default"} initialMessage=${initialMessage ? "yes" : "no"}`);
|
|
8682
|
+
const conversationNodeId = cacheKey ? getConversationIdForSession(cacheKey) : void 0;
|
|
8635
8683
|
const { response, claudeSessionId } = await performSpawnWithInitialMessage({
|
|
8636
8684
|
senderId,
|
|
8637
8685
|
userId,
|
|
@@ -8641,7 +8689,8 @@ app15.post("/", async (c) => {
|
|
|
8641
8689
|
hidden: false,
|
|
8642
8690
|
specialist,
|
|
8643
8691
|
model,
|
|
8644
|
-
initialMessage
|
|
8692
|
+
initialMessage,
|
|
8693
|
+
conversationNodeId
|
|
8645
8694
|
});
|
|
8646
8695
|
mintTranscriptEdge(
|
|
8647
8696
|
cacheKey ? getConversationIdForSession(cacheKey) : void 0,
|
|
@@ -8719,7 +8768,13 @@ app15.post("/resume", async (c) => {
|
|
|
8719
8768
|
dormantPlugins,
|
|
8720
8769
|
activePlugins,
|
|
8721
8770
|
specialistDomains,
|
|
8722
|
-
tunnelUrl
|
|
8771
|
+
tunnelUrl,
|
|
8772
|
+
// Task 382 — resume parity with /spawn: thread the active
|
|
8773
|
+
// conversationId so the re-spawned PTY's MCP env carries
|
|
8774
|
+
// CONVERSATION_NODE_ID. Undefined when no active conversation is
|
|
8775
|
+
// bound to this cacheKey (rare for resume — the user is by
|
|
8776
|
+
// definition continuing a known conversation).
|
|
8777
|
+
conversationNodeId: getConversationIdForSession(cacheKey)
|
|
8723
8778
|
})
|
|
8724
8779
|
}).catch((err) => {
|
|
8725
8780
|
console.error(`${TAG18} fetch-failed op=resume message=${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -12133,7 +12188,11 @@ app32.post("/", requireAdminSession, async (c) => {
|
|
|
12133
12188
|
hidden: true,
|
|
12134
12189
|
specialist: "database-operator",
|
|
12135
12190
|
model: void 0,
|
|
12136
|
-
initialMessage
|
|
12191
|
+
initialMessage,
|
|
12192
|
+
// Task 382 — LinkedIn ingest is a hidden autonomous spawn with no
|
|
12193
|
+
// parent admin conversation; the database-operator threads
|
|
12194
|
+
// `producedByTaskId` via work-create instead. Leave undefined.
|
|
12195
|
+
conversationNodeId: void 0
|
|
12137
12196
|
});
|
|
12138
12197
|
if (!response.ok) {
|
|
12139
12198
|
const detail = await response.text().catch(() => "");
|