@rubytech/create-maxy-code 0.1.75 → 0.1.79
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/__tests__/plugin-install.test.js +2 -2
- package/dist/lib/plugin-install.js +1 -1
- package/package.json +1 -1
- package/payload/platform/config/brand.json +1 -1
- package/payload/platform/lib/graph-write/dist/index.js +1 -1
- package/payload/platform/lib/graph-write/src/index.ts +1 -1
- package/payload/platform/package.json +1 -1
- package/payload/platform/plugins/.claude-plugin/marketplace.json +101 -1
- package/payload/platform/plugins/admin/mcp/dist/index.js +1 -1
- package/payload/platform/plugins/admin/skills/commitment-followthrough/SKILL.md +1 -1
- package/payload/platform/plugins/admin/skills/session-management/SKILL.md +1 -1
- package/payload/platform/plugins/business-assistant/PLUGIN.md +1 -1
- package/payload/platform/plugins/business-assistant/references/task-management.md +13 -13
- package/payload/platform/plugins/docs/references/admin-session.md +7 -6
- package/payload/platform/plugins/docs/references/internals.md +2 -2
- package/payload/platform/plugins/memory/PLUGIN.md +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/neo4j-password-path.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/neo4j-password-path.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/neo4j-password-path.test.js +57 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/neo4j-password-path.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/neo4j.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/neo4j.js +9 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/neo4j.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/vitest.config.ts +2 -0
- package/payload/platform/plugins/memory/skills/conversation-archive-enrich/SKILL.md +2 -2
- package/payload/platform/plugins/projects/PLUGIN.md +3 -3
- package/payload/platform/plugins/projects/references/sprint.md +2 -2
- package/payload/platform/plugins/scheduling/skills/briefing/SKILL.md +2 -2
- package/payload/platform/plugins/tasks/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/work/.claude-plugin/plugin.json +17 -0
- package/payload/platform/plugins/{tasks → work}/.mcp.json +2 -2
- package/payload/platform/plugins/work/PLUGIN.md +120 -0
- package/payload/platform/plugins/work/mcp/dist/index.d.ts +2 -0
- package/payload/platform/plugins/work/mcp/dist/index.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/index.js +543 -0
- package/payload/platform/plugins/work/mcp/dist/index.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/lib/embeddings.d.ts +7 -0
- package/payload/platform/plugins/work/mcp/dist/lib/embeddings.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/lib/embeddings.js +24 -0
- package/payload/platform/plugins/work/mcp/dist/lib/embeddings.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/lib/neo4j.d.ts +5 -0
- package/payload/platform/plugins/work/mcp/dist/lib/neo4j.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/lib/neo4j.js +40 -0
- package/payload/platform/plugins/work/mcp/dist/lib/neo4j.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-complete.d.ts +17 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-complete.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-complete.js +76 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-complete.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-create.d.ts +29 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-create.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-create.js +235 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-create.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-get.d.ts +40 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-get.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-get.js +125 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-get.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-list.d.ts +26 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-list.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-list.js +81 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-list.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-update.d.ts +19 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-update.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-update.js +102 -0
- package/payload/platform/plugins/work/mcp/dist/tools/project-update.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/session-list.d.ts +20 -0
- package/payload/platform/plugins/work/mcp/dist/tools/session-list.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/session-list.js +37 -0
- package/payload/platform/plugins/work/mcp/dist/tools/session-list.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/session-name.d.ts +12 -0
- package/payload/platform/plugins/work/mcp/dist/tools/session-name.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/session-name.js +28 -0
- package/payload/platform/plugins/work/mcp/dist/tools/session-name.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-complete.d.ts +16 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-complete.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-complete.js +33 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-complete.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-create.d.ts +63 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-create.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-create.js +141 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-create.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-get.d.ts +19 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-get.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-get.js +51 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-get.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-list.d.ts +18 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-list.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-list.js +66 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-list.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-ready.d.ts +21 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-ready.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-ready.js +54 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-ready.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-relate.d.ts +12 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-relate.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-relate.js +59 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-relate.js.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-update.d.ts +32 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-update.d.ts.map +1 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-update.js +112 -0
- package/payload/platform/plugins/work/mcp/dist/tools/work-update.js.map +1 -0
- package/payload/platform/plugins/{tasks → work}/mcp/package.json +1 -1
- package/payload/platform/scripts/conversation-id-allowlist.txt +4 -4
- package/payload/platform/scripts/seed-neo4j.sh +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 +34 -5
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/index.js +32 -0
- package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +33 -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 +170 -39
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/specialist-drift.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/specialist-drift.js +10 -4
- package/payload/platform/services/claude-session-manager/dist/specialist-drift.js.map +1 -1
- package/payload/platform/templates/agents/admin/IDENTITY.md +21 -37
- package/payload/platform/templates/specialists/agents/database-operator.md +1 -1
- package/payload/platform/templates/specialists/agents/librarian.md +1 -1
- package/payload/platform/templates/specialists/agents/personal-assistant.md +1 -1
- package/payload/platform/templates/specialists/agents/project-manager.md +3 -3
- package/payload/premium-plugins/real-agent/plugins/leads/PLUGIN.md +1 -1
- package/payload/server/{chunk-ITQ55PCQ.js → chunk-TFEIFU7F.js} +1 -1
- package/payload/server/{chunk-HZ77KMTV.js → chunk-W33UUYYR.js} +1 -4
- package/payload/server/{cloudflare-task-tracker-PZBX7X3D.js → cloudflare-task-tracker-7P355PSD.js} +1 -1
- package/payload/server/maxy-edge.js +1 -1
- package/payload/server/server.js +50 -18
- package/payload/platform/plugins/tasks/PLUGIN.md +0 -120
|
@@ -7,7 +7,7 @@ import assert from "node:assert/strict";
|
|
|
7
7
|
import { parsePluginList, computeInstallActions, computeConfigureActions, parseExternalPlugins, } from "../lib/plugin-install.js";
|
|
8
8
|
test("parsePluginList extracts name@marketplace tuples", () => {
|
|
9
9
|
const stdout = `
|
|
10
|
-
❯
|
|
10
|
+
❯ work@maxy-platform
|
|
11
11
|
Version: 0.1.0
|
|
12
12
|
Scope: local
|
|
13
13
|
|
|
@@ -16,7 +16,7 @@ test("parsePluginList extracts name@marketplace tuples", () => {
|
|
|
16
16
|
`;
|
|
17
17
|
const out = parsePluginList(stdout);
|
|
18
18
|
assert.deepEqual(out, [
|
|
19
|
-
{ name: "
|
|
19
|
+
{ name: "work", marketplace: "maxy-platform" },
|
|
20
20
|
{ name: "telegram", marketplace: "claude-plugins-official" },
|
|
21
21
|
]);
|
|
22
22
|
});
|
package/package.json
CHANGED
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
},
|
|
48
48
|
|
|
49
49
|
"plugins": {
|
|
50
|
-
"core": ["admin", "memory", "docs", "cloudflare", "anthropic", "workflows", "
|
|
50
|
+
"core": ["admin", "memory", "docs", "cloudflare", "anthropic", "workflows", "work", "scheduling", "email", "contacts", "projects"],
|
|
51
51
|
"defaultEnabled": ["business-assistant", "sales"],
|
|
52
52
|
"available": ["telegram", "waitlist", "deep-research", "whatsapp", "replicate", "linkedin-import"],
|
|
53
53
|
"excluded": []
|
|
@@ -222,7 +222,7 @@ async function writeNodeWithEdges(params) {
|
|
|
222
222
|
.filter((m) => m !== null);
|
|
223
223
|
if (matched.length === 0) {
|
|
224
224
|
process.stderr.write(`[graph-write] reject reason=missing-provenance labels=${labelCsv} agent=${agentLabel}\n`);
|
|
225
|
-
throw new Error(`missing-provenance: write to ${labelCsv} requires an inbound :PRODUCED edge from a :Task, :Conversation, or :Message (createdBy.agent='${agentLabel}'). Either pass producedByTaskId on the write (autonomous workflows: call
|
|
225
|
+
throw new Error(`missing-provenance: write to ${labelCsv} requires an inbound :PRODUCED edge from a :Task, :Conversation, or :Message (createdBy.agent='${agentLabel}'). Either pass producedByTaskId on the write (autonomous workflows: call work-create at the start of the flow and thread the returned taskId), or rely on the MCP wrapper's CONVERSATION_NODE_ID env-stamp injection (direct admin asks).`);
|
|
226
226
|
}
|
|
227
227
|
provenanceSourceId = matched[0].rel.targetNodeId;
|
|
228
228
|
provenanceSourceLabel = matched[0].sourceLabel;
|
|
@@ -300,7 +300,7 @@ export async function writeNodeWithEdges(
|
|
|
300
300
|
`[graph-write] reject reason=missing-provenance labels=${labelCsv} agent=${agentLabel}\n`
|
|
301
301
|
);
|
|
302
302
|
throw new Error(
|
|
303
|
-
`missing-provenance: write to ${labelCsv} requires an inbound :PRODUCED edge from a :Task, :Conversation, or :Message (createdBy.agent='${agentLabel}'). Either pass producedByTaskId on the write (autonomous workflows: call
|
|
303
|
+
`missing-provenance: write to ${labelCsv} requires an inbound :PRODUCED edge from a :Task, :Conversation, or :Message (createdBy.agent='${agentLabel}'). Either pass producedByTaskId on the write (autonomous workflows: call work-create at the start of the flow and thread the returned taskId), or rely on the MCP wrapper's CONVERSATION_NODE_ID env-stamp injection (direct admin asks).`
|
|
304
304
|
);
|
|
305
305
|
}
|
|
306
306
|
provenanceSourceId = matched[0].rel.targetNodeId;
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"build:telegram": "tsc -p plugins/telegram/mcp/tsconfig.json",
|
|
14
14
|
"build:admin": "tsc -p plugins/admin/mcp/tsconfig.json",
|
|
15
15
|
"build:cloudflare": "tsc -p plugins/cloudflare/mcp/tsconfig.json",
|
|
16
|
-
"build:
|
|
16
|
+
"build:work": "tsc -p plugins/work/mcp/tsconfig.json",
|
|
17
17
|
"build:email": "tsc -p plugins/email/mcp/tsconfig.json",
|
|
18
18
|
"build:outlook": "tsc -p plugins/outlook/mcp/tsconfig.json",
|
|
19
19
|
"build:workflows": "tsc -p plugins/workflows/mcp/tsconfig.json",
|
|
@@ -1,14 +1,114 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "maxy-
|
|
2
|
+
"name": "maxy-platform",
|
|
3
3
|
"owner": {
|
|
4
4
|
"name": "Rubytech LLC"
|
|
5
5
|
},
|
|
6
6
|
"plugins": [
|
|
7
|
+
{
|
|
8
|
+
"name": "admin",
|
|
9
|
+
"source": "./admin",
|
|
10
|
+
"version": "0.1.0"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"name": "business-assistant",
|
|
14
|
+
"source": "./business-assistant",
|
|
15
|
+
"version": "0.1.0"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"name": "cloudflare",
|
|
19
|
+
"source": "./cloudflare",
|
|
20
|
+
"version": "0.1.0"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"name": "contacts",
|
|
24
|
+
"source": "./contacts",
|
|
25
|
+
"version": "0.1.0"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"name": "deep-research",
|
|
29
|
+
"source": "./deep-research",
|
|
30
|
+
"version": "0.1.0"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"name": "docs",
|
|
34
|
+
"source": "./docs",
|
|
35
|
+
"version": "0.1.0"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"name": "email",
|
|
39
|
+
"source": "./email",
|
|
40
|
+
"version": "0.1.0"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"name": "linkedin-import",
|
|
44
|
+
"source": "./linkedin-import",
|
|
45
|
+
"version": "0.1.0"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"name": "memory",
|
|
49
|
+
"source": "./memory",
|
|
50
|
+
"version": "0.1.0"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"name": "outlook",
|
|
54
|
+
"source": "./outlook",
|
|
55
|
+
"version": "0.1.0"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"name": "projects",
|
|
59
|
+
"source": "./projects",
|
|
60
|
+
"version": "0.1.0"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"name": "replicate",
|
|
64
|
+
"source": "./replicate",
|
|
65
|
+
"version": "0.1.0"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"name": "sales",
|
|
69
|
+
"source": "./sales",
|
|
70
|
+
"version": "0.1.0"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"name": "scheduling",
|
|
74
|
+
"source": "./scheduling",
|
|
75
|
+
"version": "0.1.0"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"name": "tasks",
|
|
79
|
+
"source": "./tasks",
|
|
80
|
+
"version": "0.1.0"
|
|
81
|
+
},
|
|
7
82
|
{
|
|
8
83
|
"name": "teaching",
|
|
9
84
|
"source": "./teaching",
|
|
10
85
|
"version": "0.1.0"
|
|
11
86
|
},
|
|
87
|
+
{
|
|
88
|
+
"name": "telegram",
|
|
89
|
+
"source": "./telegram",
|
|
90
|
+
"version": "0.1.0"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"name": "waitlist",
|
|
94
|
+
"source": "./waitlist",
|
|
95
|
+
"version": "0.1.0"
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"name": "whatsapp",
|
|
99
|
+
"source": "./whatsapp",
|
|
100
|
+
"version": "0.1.0"
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"name": "work",
|
|
104
|
+
"source": "./work",
|
|
105
|
+
"version": "0.1.0"
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"name": "workflows",
|
|
109
|
+
"source": "./workflows",
|
|
110
|
+
"version": "0.1.0"
|
|
111
|
+
},
|
|
12
112
|
{
|
|
13
113
|
"name": "writer-craft",
|
|
14
114
|
"source": "./writer-craft",
|
|
@@ -1759,7 +1759,7 @@ eagerTool(server, "skill-load", "Load a plugin skill's SKILL.md body by skill na
|
|
|
1759
1759
|
if (requiredInputs) {
|
|
1760
1760
|
const slotLabels = requiredInputs.map(s => s.join(" | ")).join(", ");
|
|
1761
1761
|
const marker = JSON.stringify(requiredInputs);
|
|
1762
|
-
const notice = `<!-- skill-input-contract requiredInputs=${marker} -->\nREQUIRED INPUTS: ${slotLabels}\nIf the operator has not supplied each of these inputs, your next tool call MUST be AskUserQuestion. Do not use memory-search, conversation-search, graph-read, or
|
|
1762
|
+
const notice = `<!-- skill-input-contract requiredInputs=${marker} -->\nREQUIRED INPUTS: ${slotLabels}\nIf the operator has not supplied each of these inputs, your next tool call MUST be AskUserQuestion. Do not use memory-search, conversation-search, graph-read, or work-get to guess the missing input.\n<!-- /skill-input-contract -->\n\n`;
|
|
1763
1763
|
bodyOut = notice + content;
|
|
1764
1764
|
}
|
|
1765
1765
|
const ms = Date.now() - start;
|
|
@@ -39,7 +39,7 @@ Rule 1 is yours; rule 2 is this skill's job.
|
|
|
39
39
|
Use the most appropriate tool:
|
|
40
40
|
|
|
41
41
|
- `schedule-event` for time-bound reminders (every Monday, on Tuesday, by Friday).
|
|
42
|
-
- `
|
|
42
|
+
- `work-create` for open-ended obligations (chase X, finish Y, prepare Z).
|
|
43
43
|
- `workflow-create` for multi-step processes that recur (monthly close-out, new-instruction onboarding).
|
|
44
44
|
|
|
45
45
|
## Verify executor capabilities before promising a workflow
|
|
@@ -20,7 +20,7 @@ This skill wraps the session tools and carries the discipline that keeps long-ru
|
|
|
20
20
|
|
|
21
21
|
When the owner asks to start a new session, clear the conversation, or reset, call `session-reset`. Do not ask for confirmation. After the tool call, say nothing further; the UI clears and returns to idle.
|
|
22
22
|
|
|
23
|
-
**Before suggesting a reset (e.g. after a plugin activation, when context has drifted), persist every actionable finding from the current conversation to its durable store first.** Compaction saves a session summary, but summaries are lossy: decisions, working state, and unfinished threads must be captured explicitly before the context window is cleared. Write open tasks via `
|
|
23
|
+
**Before suggesting a reset (e.g. after a plugin activation, when context has drifted), persist every actionable finding from the current conversation to its durable store first.** Compaction saves a session summary, but summaries are lossy: decisions, working state, and unfinished threads must be captured explicitly before the context window is cleared. Write open tasks via `work-create`, profile updates via `profile-update`, and any other graph state. Then tell the owner what will carry into the new session (the summary and the open tasks via `<previous-context>`) and suggest the reset.
|
|
24
24
|
|
|
25
25
|
## Continuing a previous session
|
|
26
26
|
|
|
@@ -39,7 +39,7 @@ Every conversation is a session. At the start of each session:
|
|
|
39
39
|
3. Call `session-name` with the current `sessionKey` and a short label once the session's purpose is clear
|
|
40
40
|
4. Surface any open tasks to the owner before asking what's new
|
|
41
41
|
|
|
42
|
-
When the owner asks about sessions, tasks, or what's in progress — use `session-list` and `
|
|
42
|
+
When the owner asks about sessions, tasks, or what's in progress — use `session-list` and `work-list`.
|
|
43
43
|
|
|
44
44
|
## Industry Overlays
|
|
45
45
|
|
|
@@ -37,10 +37,10 @@ Do not create tasks for simple one-turn exchanges (answering a question, looking
|
|
|
37
37
|
|
|
38
38
|
### Creating a task
|
|
39
39
|
|
|
40
|
-
Use `
|
|
40
|
+
Use `work-create`. Always pass `sessionKey` (from your system prompt — "Current session key: ...") to link the task to the current conversation automatically:
|
|
41
41
|
|
|
42
42
|
```
|
|
43
|
-
|
|
43
|
+
work-create {
|
|
44
44
|
name: "Telegram setup",
|
|
45
45
|
description: "Configure Telegram bot and webhook for the business",
|
|
46
46
|
status: "active",
|
|
@@ -51,10 +51,10 @@ task-create {
|
|
|
51
51
|
|
|
52
52
|
### Updating a task
|
|
53
53
|
|
|
54
|
-
Use `
|
|
54
|
+
Use `work-update` to change name, description, status, priority, due date, or append a progress note:
|
|
55
55
|
|
|
56
56
|
```
|
|
57
|
-
|
|
57
|
+
work-update {
|
|
58
58
|
taskId: "<id>",
|
|
59
59
|
name: "Corrected task name",
|
|
60
60
|
note: "Renamed to reflect actual scope"
|
|
@@ -62,7 +62,7 @@ task-update {
|
|
|
62
62
|
```
|
|
63
63
|
|
|
64
64
|
```
|
|
65
|
-
|
|
65
|
+
work-update {
|
|
66
66
|
taskId: "<id>",
|
|
67
67
|
status: "completed",
|
|
68
68
|
note: "Webhook configured and verified"
|
|
@@ -73,10 +73,10 @@ Notes are append-only — never overwritten. Name and description changes preser
|
|
|
73
73
|
|
|
74
74
|
### Completing a task
|
|
75
75
|
|
|
76
|
-
Use `
|
|
76
|
+
Use `work-complete` as a shorthand — it sets status to `completed` and appends a closing note:
|
|
77
77
|
|
|
78
78
|
```
|
|
79
|
-
|
|
79
|
+
work-complete {
|
|
80
80
|
taskId: "<id>",
|
|
81
81
|
note: "Telegram bot live, webhook responding"
|
|
82
82
|
}
|
|
@@ -131,14 +131,14 @@ Returns recent conversations with their names, session keys, and any linked task
|
|
|
131
131
|
### List open tasks
|
|
132
132
|
|
|
133
133
|
```
|
|
134
|
-
|
|
135
|
-
|
|
134
|
+
work-list { status: "active" }
|
|
135
|
+
work-list { status: "pending" }
|
|
136
136
|
```
|
|
137
137
|
|
|
138
138
|
### Get a specific task
|
|
139
139
|
|
|
140
140
|
```
|
|
141
|
-
|
|
141
|
+
work-get { taskId: "<id>" }
|
|
142
142
|
```
|
|
143
143
|
|
|
144
144
|
---
|
|
@@ -147,10 +147,10 @@ task-get { taskId: "<id>" }
|
|
|
147
147
|
|
|
148
148
|
If a user returns to work on a previous task:
|
|
149
149
|
1. Call `session-list` to find the original session and task
|
|
150
|
-
2. Call `
|
|
151
|
-
3. Pass `sessionKey` in `
|
|
150
|
+
2. Call `work-update` to set `status: "active"` and note the resumption
|
|
151
|
+
3. Pass `sessionKey` in `work-create` for any new tasks in the new session — it links them to the current conversation automatically
|
|
152
152
|
|
|
153
|
-
A task can span multiple sessions. Use `
|
|
153
|
+
A task can span multiple sessions. Use `work-create` with `sessionKey` each time work resumes in a new conversation.
|
|
154
154
|
|
|
155
155
|
---
|
|
156
156
|
|
|
@@ -119,18 +119,19 @@ The hook emits two summary lines per fire immediately before `spawn-request`:
|
|
|
119
119
|
- `[turn-recorder] envelope sessionId=<op> turnsCount=<n> userTurns=<n> assistantTurns=<n> toolCallTurns=<n>` — the walker's output. `turnsCount` is monotone across spawns in the same operator session; a flat or decreasing series is the regression signature for "windowing crept back in".
|
|
120
120
|
- `[turn-recorder] substitution sessionId=<op> schemaBytes=<n> conversationBytes=<n> bodyBytes=<n>` — the per-spawn substitution. `schemaBytes` covers schema-base plus any vertical concatenated. `bodyBytes` equals the trailing `initialMessageBytes` field on the next `spawn-request` line — the two log lines read the same number.
|
|
121
121
|
|
|
122
|
-
**Manager-side specialist branches.** The recorder is the first specialist subagent that exercises the full specialist flag matrix:
|
|
122
|
+
**Manager-side specialist branches (post-Task-207).** The recorder is the first specialist subagent that exercises the full specialist flag matrix:
|
|
123
123
|
- The bundled `platform/templates/specialists/agents/database-operator.md` template is symlinked into `$CLAUDE_CONFIG_DIR/agents/database-operator.md` by the installer; without that link `claude --agent database-operator` silently falls back to the admin agent.
|
|
124
124
|
- `--append-system-prompt` is omitted entirely — the agent file IS the prompt; admin IDENTITY / SOUL / aboutOwner do NOT layer on top.
|
|
125
|
-
- `--allowed-tools` is
|
|
126
|
-
- The per-spawn `mcp.json` is
|
|
125
|
+
- `--allowed-tools` is **not** pushed to argv. `--agent <name>` resolves the agent file's `tools:` frontmatter natively against Claude Code's plugin-tool registry; the argv push (Task 165) was empirically redundant and namespace-broke after Task 203's `mcp__plugin_…` rename (Task 207).
|
|
126
|
+
- The per-spawn `mcp.json` is **not** written and `--mcp-config` is **not** pushed. Plugin manifests register the MCPs at session start (Tasks 202+203). The per-account env that the JSON used to inject (`ACCOUNT_ID`, `USER_ID`, `NEO4J_URI`, `NEO4J_PASSWORD`, `PLATFORM_ROOT`, `CLAUDE_CONFIG_DIR`) now rides the PTY env block directly — see [`mcp-servers.md`](mcp-servers.md) §3 (Task 207).
|
|
127
|
+
- `--permission-mode bypassPermissions` is auto-pushed when the caller has not set `permissionMode`. Headless specialists have no operator to consent to write tools; default mode blocks them (Task 207).
|
|
127
128
|
- `--remote-control` is **not** attached and the URL-capture handler is not registered. Remote Control is channel-facing only; the recorder has no human in the loop, so no `claude.ai/code/session_<id>` URL is ever emitted and the per-byte URL regex consumes no PTY output. The spawn log line carries `remoteControl=false`. Channel-facing specialists (content-producer, librarian, personal-assistant, project-manager, research-assistant) keep `--remote-control`; only specialists named in `HEADLESS_ROLES` (`database-operator`, `memory-classify`, `commitment`, `classifier`) drop it.
|
|
128
129
|
|
|
129
130
|
**End-of-turn auto-archive.** When any session whose specialist is `database-operator` gains an assistant message with `stop_reason: "end_turn"` in its JSONL, the manager posts `/<sid>/stop` against itself, claude exits, the fs-watcher observes the PID-file removal and drops the row from the index. The auto-archive log line is `auto-archive sessionId=<sid> specialist=database-operator reason=end-turn …`. Every operator-request stop (auto-archive included) now ends in `master-fd=closed` because `stopSession()` calls `pty.destroy()` explicitly before `store.remove` (Task 170 — see `platform.md` "stopSession fd contract" for the failure mode this closes).
|
|
130
131
|
|
|
131
132
|
**Hook recursion gate.** `pty-spawner` stamps `MAXY_SPECIALIST=<specialist>` on every PTY env. The Stop hook short-circuits when `MAXY_SPECIALIST=database-operator` so the recorder's own end-of-turn does not re-fire the hook.
|
|
132
133
|
|
|
133
|
-
**Observability.** The hook emits exactly two lines per operator turn via `/api/admin/log-ingest`: `[turn-recorder] trigger sessionId=<op> turnIndex=0 transcriptBytes=<n> conversationBytes=<n>` and `[turn-recorder] spawn-request sessionId=<op> specialist=database-operator initialMessageBytes=<n>`. The manager adds `pty-spawn-allowlist specialist=database-operator count=
|
|
134
|
+
**Observability (post-Task-207).** The hook emits exactly two lines per operator turn via `/api/admin/log-ingest`: `[turn-recorder] trigger sessionId=<op> turnIndex=0 transcriptBytes=<n> conversationBytes=<n>` and `[turn-recorder] spawn-request sessionId=<op> specialist=database-operator initialMessageBytes=<n>`. The manager adds `pty-spawn-allowlist specialist=database-operator source=native-agent-flag count=skipped stripped=0`, `pty-spawn-mcp-config specialist=database-operator source=plugin-manifests`, `pty-spawn-start … specialist=database-operator append-system-prompt-bytes=0 env-keys=ACCOUNT_ID,USER_ID,PLATFORM_ROOT,CLAUDE_CONFIG_DIR,NEO4J_URI,NEO4J_PASSWORD` before the PTY launches, and one `[pty-spawn-tool-inventory] sessionId=<rec> specialist=database-operator argv-tools=0 mcp-listed-tools=<n> exposed=<csv> not-exposed=<csv>` line per spawn once the shadow probe of each MCP server returns. The remainder of the recorder lifecycle is covered by the Sidebar's existing lines (`pty-spawned`, JSONL events, `auto-archive`). Failure-mode names: `trigger-skipped reason=…` enumerates `role-not-admin | is-recorder | empty-stdin | missing-transcript | conversation-empty`. Hook-side spawn errors emit `[turn-recorder] spawn-failed reason=loopback-http http=<code>`. Probe-side failure on the inventory line emits `[pty-spawn-tool-inventory-failed] sessionId=<rec> specialist=<name> err=<json>`.
|
|
134
135
|
|
|
135
136
|
### Recorder lifecycle diagnostic
|
|
136
137
|
|
|
@@ -139,8 +140,8 @@ When the operator reports "the recorder did nothing this turn", run these seven
|
|
|
139
140
|
1. **Stop hook fired.** `grep '\[turn-recorder\] trigger sessionId=<op>' ~/.<brand>/logs/server.log` — expects one line of shape `[turn-recorder] trigger sessionId=<op> turnIndex=0 transcriptBytes=<n> conversationBytes=<n>`. Absent: the Stop hook didn't run; check that `MAXY_SESSION_ROLE=admin` was set on the operator PTY and that `MAXY_SPECIALIST!=database-operator` (recursion gate).
|
|
140
141
|
2. **/spawn accepted.** `grep '\[turn-recorder\] spawn-request sessionId=<op>' ~/.<brand>/logs/server.log` — expects `[turn-recorder] spawn-request sessionId=<op> specialist=database-operator initialMessageBytes=<n>`. The response body of this POST carries `<rec>`. Absent: the hook fired but `/api/admin/claude-sessions` rejected; look for `[turn-recorder] spawn-failed reason=loopback-http http=<code>` on the next line.
|
|
141
142
|
3. **PTY started.** `grep 'pty-spawn-start .* specialist=database-operator' ~/.<brand>/logs/server.log` — expects `pty-spawn-start claudeBin=<…> argv-count=<n> append-system-prompt-bytes=0 … specialist=database-operator prompt-positional=yes prompt-bytes=<n>`. Absent: the wrapper accepted but the manager rejected before exec; check the spawn-failure surfaces (`which-claude-not-found | pty-spawn-failed | pid-file-timeout | host-context-unresolved | identity-unresolved | mcp-config-write-failed`).
|
|
142
|
-
4. **Agent file resolved.** `grep 'pty-spawn-allowlist specialist=database-operator
|
|
143
|
-
4b. **Tool inventory exposed to the model.** `grep '\[pty-spawn-tool-inventory\] sessionId=<rec>' ~/.<brand>/logs/server.log` — expects one line of shape `[pty-spawn-tool-inventory] sessionId=<rec> specialist=database-operator argv-tools=
|
|
143
|
+
4. **Agent file resolved.** `grep 'pty-spawn-allowlist specialist=database-operator source=native-agent-flag' ~/.<brand>/logs/server.log` — expects exactly one line per recorder spawn of shape `pty-spawn-allowlist specialist=database-operator source=native-agent-flag count=skipped stripped=<n>`. `count=skipped` is structural: post-Task-207 the agent file's `tools:` frontmatter is resolved natively by `--agent`, never re-stamped onto argv. `stripped>0` means the brand-aware drift filter (Task 173) dropped tools from the agent's declared set. Absent line: the agent file at `$CLAUDE_CONFIG_DIR/agents/database-operator.md` is missing the `tools:` line, or the symlink the installer creates never landed. Re-run the installer; cross-reference `brand.json#plugins.excluded` against the agent frontmatter.
|
|
144
|
+
4b. **Tool inventory exposed to the model.** `grep '\[pty-spawn-tool-inventory\] sessionId=<rec>' ~/.<brand>/logs/server.log` — expects one line of shape `[pty-spawn-tool-inventory] sessionId=<rec> specialist=database-operator argv-tools=0 mcp-listed-tools=<n> exposed=<csv> not-exposed=<csv>`. The line lands once per spawn after the manager's shadow probe finishes a `tools/list` against each MCP server referenced by the agent's frontmatter (descriptors re-sourced from `deps.toolSurface.mcpServers` post-Task-207). The four fields decode the recorder's runtime tool surface directly: `argv-tools=0` is the literal truth — specialists no longer push `--allowed-tools` (Task 207); `mcp-listed-tools` is the sum across servers of names the probe captured; `exposed` is the intersection (in frontmatter order); `not-exposed` is the frontmatter list minus `exposed`. The regression query for "model says no tools" is `mcp-listed-tools=0` — that means registry #1 lost the platform plugins again (re-check the Task 203 marketplace-manifest gate). Absent line: probe is fire-and-forget, so absence either means the spawn was not a specialist (operator chat) or the probe itself crashed — in which case `[pty-spawn-tool-inventory-failed] sessionId=<rec> specialist=… err=…` is the partner line to grep.
|
|
144
145
|
5. **Graph write outcome.** `grep '\[mcp:memory\] memory-write .* session=<rec>' ~/.<brand>/logs/server.log` — expects one line ending `result=ok elementId=<id>` or `result=error reason=<slug>` (slug enumerated by the memory MCP write-path observability work). Absent: the recorder loaded but produced no tool calls — model-side decision, not infra. Read the recorder JSONL at `<accountDir>/.claude/projects/<slug>/<rec>.jsonl` to see what the LLM did.
|
|
145
146
|
6. **Auto-archive.** `grep 'auto-archive .* sessionId=<rec> .* specialist=database-operator reason=end-turn' ~/.<brand>/logs/server.log` — expects one line. Absent: the recorder finished but the manager's end-turn watcher didn't fire; the fs-watcher row will get reaped on its TTL but the recorder hung longer than expected. Investigate `pty-spawn-stop` and `pid-file-removed` lines on the same `<rec>`.
|
|
146
147
|
|
|
@@ -541,9 +541,9 @@ Every durable action — cloudflare tunnel-login, brand publish, future determin
|
|
|
541
541
|
|
|
542
542
|
The doctrine is enforced at the storage primitive: writes to `:Person`, `:UserProfile`, `:AdminUser`, `:Organization`, `:LocalBusiness`, `:CloudflareTunnel`, or `:CloudflareHostname` MUST include at least one inbound `:PRODUCED` edge whose source is one of `:Task`, `:Conversation`, or `:Message`. Subtype labels like `:AdminConversation`, `:UserMessage`, `:AssistantMessage`, `:AdminMessage` qualify because the gate checks the full `labels()` array. Bootstrap writes (PIN-setup, schema migrations, lazy first-session UserProfile creation) are exempt via `createdBy.agent === 'system'`.
|
|
543
543
|
|
|
544
|
-
Two surfaces emit the lifecycle: agent-driven actions call `
|
|
544
|
+
Two surfaces emit the lifecycle: agent-driven actions call `work-create`/`work-update`/`work-complete` over MCP (`work-create` accepts `kind`, the canonical `inputsProvided` call-shape record, `inputs` + `inputSchema` for the operator-meaningful form payload, and `raisedDuringConversationKey` to resolve the `RAISED_DURING` edge). Shell-driven actions wrap their script invocation in [platform/ui/app/lib/cloudflare-task-tracker.ts](../../../ui/app/lib/cloudflare-task-tracker.ts) (cloudflare is the first; installer / brand-publish / OAuth-login deferred). Both surfaces emit the same `[task] action-start|step|done` log lines so operators can grep one channel uniformly. Both also call the central `redactSecrets` primitive ([platform/lib/task-secrets/](../../../lib/task-secrets/)) to strip schema-tagged secret keys before persisting `inputs.<field>` props on the Task — see `.docs/neo4j.md § Audit Task input contract` for the contract that replaces per-kind allow-lists.
|
|
545
545
|
|
|
546
|
-
Two surfaces feed the gate. (1) **Workflow path:** `memory-write` accepts an optional `producedByTaskId` parameter. When set, an inbound `:PRODUCED` edge from that Task is composed into the write's relationships before the gate runs — the typical agent-side pattern is to call `
|
|
546
|
+
Two surfaces feed the gate. (1) **Workflow path:** `memory-write` accepts an optional `producedByTaskId` parameter. When set, an inbound `:PRODUCED` edge from that Task is composed into the write's relationships before the gate runs — the typical agent-side pattern is to call `work-create` at the start of an autonomous flow, capture `taskId`, and pass it as `producedByTaskId` on every subsequent `memory-write` for a gated label. The gate verifies Task and write share the same `accountId`; mismatch is rejected loud. (2) **Direct-ask path:** the admin server resolves the active `:AdminConversation` elementId at MCP spawn time and stamps it as `CONVERSATION_NODE_ID` in the env. The `contact-create` and `memory-write` wrappers call `injectConversationProvenance` (exported from [`@maxy/graph-write`](../../../lib/graph-write/src/conversation-provenance.ts)) which verifies `accountId` parity and prepends the synthetic `:PRODUCED` edge — no agent-visible schema field changes. `memory-write` uses the env-stamp only as a fallback when `producedByTaskId` is unset; `contact-create` has no `producedByTaskId` parameter today and relies on the env-stamp alone.
|
|
547
547
|
|
|
548
548
|
Operator audit cyphers:
|
|
549
549
|
- "What entities did this conversation's actions produce?" — `MATCH (c:AdminConversation {conversationId:$id})<-[:RAISED_DURING]-(t:Task)-[:PRODUCED]->(e) RETURN labels(e), e.name, t.kind, t.status`
|
|
@@ -140,7 +140,7 @@ Every node in the graph sits under a parent in the canonical chain `LocalBusines
|
|
|
140
140
|
|
|
141
141
|
Writes targeting `:Person`, `:UserProfile`, `:AdminUser`, `:Organization`, `:LocalBusiness`, `:CloudflareTunnel`, or `:CloudflareHostname` require an inbound `:PRODUCED` edge whose source is one of `:Task`, `:Conversation`, or `:Message` (deterministic bootstrap paths run as `createdBy.agent === 'system'` and are exempt). Two surfaces feed the gate:
|
|
142
142
|
|
|
143
|
-
- **Workflow path — `producedByTaskId`.** `memory-write` accepts an optional `producedByTaskId` parameter. When set, the platform composes an inbound `:PRODUCED` edge from that `:Task` node into the write's `relationships` array before the write runs. The Task and the new node must share the same `accountId`; mismatch is rejected loud. The typical pattern: call `
|
|
143
|
+
- **Workflow path — `producedByTaskId`.** `memory-write` accepts an optional `producedByTaskId` parameter. When set, the platform composes an inbound `:PRODUCED` edge from that `:Task` node into the write's `relationships` array before the write runs. The Task and the new node must share the same `accountId`; mismatch is rejected loud. The typical pattern: call `work-create` at the start of an autonomous flow (onboarding skill, cloudflare tunnel-login) with `kind` and `raisedDuringConversationKey`; capture the returned `taskId`; pass it as `producedByTaskId` on every subsequent `memory-write` for one of the gated labels.
|
|
144
144
|
|
|
145
145
|
- **Direct-ask path — `CONVERSATION_NODE_ID` env-stamp.** When `producedByTaskId` is not provided (typical of direct admin asks like "add Anneke as person"), `memory-write` falls back to the `CONVERSATION_NODE_ID` env var that the admin server stamps at MCP spawn time. The value is the elementId of the active `:AdminConversation`. The wrapper verifies `accountId` parity before injecting the synthetic `:PRODUCED` edge — `accountId` mismatch is logged as `[mcp:memory] [provenance-missing] reason=account-mismatch` and the gate then fires `missing-provenance`. The env-stamp is invisible to the agent (no schema field changes); the LLM never decides whether to use it.
|
|
146
146
|
|
package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/neo4j-password-path.test.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"neo4j-password-path.test.d.ts","sourceRoot":"","sources":["../../../src/lib/__tests__/neo4j-password-path.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
2
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { join, resolve } from "node:path";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
// Task 207 — `readPassword()` (memory plugin) falls back to a
|
|
6
|
+
// `<platformRoot>/config/.neo4j-password` lookup when neither
|
|
7
|
+
// `NEO4J_PASSWORD` nor `PLATFORM_ROOT` is set in env. Before this task,
|
|
8
|
+
// the fallback's `../../../..` math from `dist/lib/neo4j.js` landed at
|
|
9
|
+
// `platform/plugins/` (one level too high), so the read failed silently
|
|
10
|
+
// while the per-spawn `.mcp.json` masked the bug by always injecting
|
|
11
|
+
// `PLATFORM_ROOT`. With Task 207 dropping the per-spawn JSON for
|
|
12
|
+
// specialists, this fallback is the new load-bearing path; the path
|
|
13
|
+
// math is `../../../../..` (five up).
|
|
14
|
+
//
|
|
15
|
+
// This test verifies the resolved path lands at `<platformRoot>/config/`,
|
|
16
|
+
// not `<platformRoot>/plugins/config/`, by walking the same number of
|
|
17
|
+
// segments up from a fixture directory shaped like the dist layout.
|
|
18
|
+
describe("Task 207 — readPassword fallback path math", () => {
|
|
19
|
+
let tmpRoot = "";
|
|
20
|
+
const originalEnv = { ...process.env };
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
tmpRoot = mkdtempSync(join(tmpdir(), "task207-neo4j-path-"));
|
|
23
|
+
delete process.env.NEO4J_PASSWORD;
|
|
24
|
+
delete process.env.PLATFORM_ROOT;
|
|
25
|
+
});
|
|
26
|
+
afterEach(() => {
|
|
27
|
+
process.env = { ...originalEnv };
|
|
28
|
+
if (tmpRoot)
|
|
29
|
+
rmSync(tmpRoot, { recursive: true, force: true });
|
|
30
|
+
});
|
|
31
|
+
it("five-segment hop from dist/lib/neo4j.js lands at platform/config/.neo4j-password", () => {
|
|
32
|
+
// Layout mirrors the compiled tree:
|
|
33
|
+
// <tmpRoot>/platform/config/.neo4j-password
|
|
34
|
+
// <tmpRoot>/platform/plugins/memory/mcp/dist/lib/neo4j.js
|
|
35
|
+
const platformRoot = join(tmpRoot, "platform");
|
|
36
|
+
mkdirSync(join(platformRoot, "config"), { recursive: true });
|
|
37
|
+
writeFileSync(join(platformRoot, "config", ".neo4j-password"), "secret-from-file\n", "utf-8");
|
|
38
|
+
// Simulate the source-of-truth math: import.meta.dirname is the
|
|
39
|
+
// directory of the compiled neo4j.js. Five segments up is platform/.
|
|
40
|
+
const distLibDir = join(platformRoot, "plugins", "memory", "mcp", "dist", "lib");
|
|
41
|
+
mkdirSync(distLibDir, { recursive: true });
|
|
42
|
+
const resolved = resolve(distLibDir, "../../../../..", "config/.neo4j-password");
|
|
43
|
+
expect(resolved).toBe(join(platformRoot, "config/.neo4j-password"));
|
|
44
|
+
expect(readFileSync(resolved, "utf-8").trim()).toBe("secret-from-file");
|
|
45
|
+
});
|
|
46
|
+
it("four-segment hop lands at platform/plugins/ which has no .neo4j-password — proves the regression case", () => {
|
|
47
|
+
const platformRoot = join(tmpRoot, "platform");
|
|
48
|
+
mkdirSync(join(platformRoot, "config"), { recursive: true });
|
|
49
|
+
writeFileSync(join(platformRoot, "config", ".neo4j-password"), "secret\n", "utf-8");
|
|
50
|
+
const distLibDir = join(platformRoot, "plugins", "memory", "mcp", "dist", "lib");
|
|
51
|
+
mkdirSync(distLibDir, { recursive: true });
|
|
52
|
+
const wrong = resolve(distLibDir, "../../../..", "config/.neo4j-password");
|
|
53
|
+
expect(wrong).toBe(join(platformRoot, "plugins", "config", ".neo4j-password"));
|
|
54
|
+
expect(existsSync(wrong)).toBe(false);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
//# sourceMappingURL=neo4j-password-path.test.js.map
|
package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/neo4j-password-path.test.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"neo4j-password-path.test.js","sourceRoot":"","sources":["../../../src/lib/__tests__/neo4j-password-path.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"neo4j.d.ts","sourceRoot":"","sources":["../../src/lib/neo4j.ts"],"names":[],"mappings":"AAAA,OAAc,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"neo4j.d.ts","sourceRoot":"","sources":["../../src/lib/neo4j.ts"],"names":[],"mappings":"AAAA,OAAc,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AA+BtD,wBAAgB,SAAS,IAAI,MAAM,CAgBlC;AAED,wBAAgB,UAAU,IAAI,OAAO,CAEpC;AAED,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAKjD"}
|
|
@@ -5,7 +5,15 @@ let driver = null;
|
|
|
5
5
|
function readPassword() {
|
|
6
6
|
if (process.env.NEO4J_PASSWORD)
|
|
7
7
|
return process.env.NEO4J_PASSWORD;
|
|
8
|
-
|
|
8
|
+
// Task 207 — five segments up from the compiled file
|
|
9
|
+
// (`platform/plugins/memory/mcp/dist/lib/neo4j.js`) reaches `platform/`,
|
|
10
|
+
// where the installer writes `config/.neo4j-password`. The previous
|
|
11
|
+
// four-segment hop landed at `platform/plugins/`, where no password
|
|
12
|
+
// file exists; that miscalculation was masked while the per-spawn
|
|
13
|
+
// `.mcp.json` injected `PLATFORM_ROOT` on every MCP child. Task 207
|
|
14
|
+
// drops that JSON for specialists, so this fallback is the new
|
|
15
|
+
// load-bearing path when `process.env.PLATFORM_ROOT` is unset.
|
|
16
|
+
const passwordFile = resolve(process.env.PLATFORM_ROOT ?? resolve(import.meta.dirname, "../../../../.."), "config/.neo4j-password");
|
|
9
17
|
try {
|
|
10
18
|
return readFileSync(passwordFile, "utf-8").trim();
|
|
11
19
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"neo4j.js","sourceRoot":"","sources":["../../src/lib/neo4j.ts"],"names":[],"mappings":"AAAA,OAAO,KAA0B,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,IAAI,MAAM,GAAkB,IAAI,CAAC;AAEjC,SAAS,YAAY;IACnB,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IAElE,MAAM,YAAY,GAAG,OAAO,CAC1B,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"neo4j.js","sourceRoot":"","sources":["../../src/lib/neo4j.ts"],"names":[],"mappings":"AAAA,OAAO,KAA0B,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,IAAI,MAAM,GAAkB,IAAI,CAAC;AAEjC,SAAS,YAAY;IACnB,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IAElE,qDAAqD;IACrD,yEAAyE;IACzE,oEAAoE;IACpE,oEAAoE;IACpE,kEAAkE;IAClE,oEAAoE;IACpE,+DAA+D;IAC/D,+DAA+D;IAC/D,MAAM,YAAY,GAAG,OAAO,CAC1B,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,gBAAgB,CAAC,EAC3E,wBAAwB,CACzB,CAAC;IAEF,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;IACpD,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CACb,yCAAyC,YAAY,gCAAgC,CACtF,CAAC;IACJ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,6DAA6D;QAC7D,oEAAoE;QACpE,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC;QAClC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,MAAM,IAAI,KAAK,CACb,yEAAyE,CAC1E,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,OAAO,CAAC;QAC/C,MAAM,QAAQ,GAAG,YAAY,EAAE,CAAC;QAChC,OAAO,CAAC,KAAK,CAAC,+BAA+B,GAAG,EAAE,CAAC,CAAC;QACpD,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,OAAO,SAAS,EAAE,CAAC,OAAO,EAAE,CAAC;AAC/B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACrB,MAAM,GAAG,IAAI,CAAC;IAChB,CAAC;AACH,CAAC"}
|
|
@@ -16,6 +16,8 @@ export default defineConfig({
|
|
|
16
16
|
include: [
|
|
17
17
|
"src/tools/__tests__/**/*.test.ts",
|
|
18
18
|
"src/lib/__tests__/live-schema-source.test.ts",
|
|
19
|
+
// Task 207 — Vitest-style test for `readPassword()` fallback path math.
|
|
20
|
+
"src/lib/__tests__/neo4j-password-path.test.ts",
|
|
19
21
|
],
|
|
20
22
|
testTimeout: 10_000,
|
|
21
23
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: conversation-archive-enrich
|
|
3
|
-
description: Source-agnostic Phase 2 for any `:ConversationArchive` — walks the `:Section:Conversation` chunks of one named archive and asks the operator to confirm derived claims row-by-row. Fires only when the operator names a specific archive and asks for insights/enrichment; never auto-fires on Phase 1 completion. Per-row gate (wire / skip / reject) over four kinds — `mention`, `task`, `preference`, `observed-relationship`. Re-runs are idempotent on `(elementId(chunk), kind, contentHash)`, so collapsing duplicates is structural. Invokes the read-only MCP tool `mcp__memory__conversation-archive-derive-insights` to obtain proposals; never writes from the tool's return path. Each `wire` decision runs the proposal's returned `mergeCypher` via `mcp__graph__maxy-graph-write_neo4j_cypher`, or, for `task`, mints the `:Task` via `
|
|
3
|
+
description: Source-agnostic Phase 2 for any `:ConversationArchive` — walks the `:Section:Conversation` chunks of one named archive and asks the operator to confirm derived claims row-by-row. Fires only when the operator names a specific archive and asks for insights/enrichment; never auto-fires on Phase 1 completion. Per-row gate (wire / skip / reject) over four kinds — `mention`, `task`, `preference`, `observed-relationship`. Re-runs are idempotent on `(elementId(chunk), kind, contentHash)`, so collapsing duplicates is structural. Invokes the read-only MCP tool `mcp__memory__conversation-archive-derive-insights` to obtain proposals; never writes from the tool's return path. Each `wire` decision runs the proposal's returned `mergeCypher` via `mcp__graph__maxy-graph-write_neo4j_cypher`, or, for `task`, mints the `:Task` via `mcp__work__work-create` first and then runs the REFERENCES merge.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Conversation Archive — chunk-anchored insight derivation
|
|
@@ -78,7 +78,7 @@ A re-run against the same `(chunkElementId, target, contentHash)` collapses on t
|
|
|
78
78
|
|
|
79
79
|
### `task`
|
|
80
80
|
|
|
81
|
-
1. Mint the `:Task` via `
|
|
81
|
+
1. Mint the `:Task` via `mcp__work__work-create` with `title=mergeParams.taskTitle`, optional due hint from `mergeParams.taskDueHint`, `affects=archiveElementId`, `raisedDuringConversationKey=chunkElementId`.
|
|
82
82
|
2. Run the proposal's `mergeCypher` via `mcp__graph__maxy-graph-write_neo4j_cypher` with `mergeParams` plus `taskElementId=<minted Task elementId>`.
|
|
83
83
|
|
|
84
84
|
The `:Task` itself is the durable artefact; the `REFERENCES` edge to the chunk records the evidence that produced it.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: projects
|
|
3
3
|
description: "Structured project execution. Phased sprints, operational investigations, deliverable reviews, and retrospectives — backed by :Project:Task graph nodes with HAS_TASK relationships, health signals, and lifecycle tracking."
|
|
4
4
|
requires:
|
|
5
|
-
-
|
|
5
|
+
- work
|
|
6
6
|
metadata: {"platform":{"always":false,"embed":["admin"],"pluginKey":"projects","optional":true,"recommended":true}}
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -41,7 +41,7 @@ Choose a tier based on scope:
|
|
|
41
41
|
- **standard** — moderate complexity, multiple phases (e.g., kitchen refit)
|
|
42
42
|
- **full** — significant scope, many dependencies (e.g., new build project)
|
|
43
43
|
|
|
44
|
-
Include work items with their dependencies when the scope is known. If the user describes work incrementally, create the project first and add tasks later via `
|
|
44
|
+
Include work items with their dependencies when the scope is known. If the user describes work incrementally, create the project first and add tasks later via `work-create` + `work-relate`.
|
|
45
45
|
|
|
46
46
|
## Checking Project Status
|
|
47
47
|
|
|
@@ -87,4 +87,4 @@ For scope changes and issues, use `project-update` with a `note` parameter follo
|
|
|
87
87
|
|
|
88
88
|
## Dependencies
|
|
89
89
|
|
|
90
|
-
This plugin requires the `
|
|
90
|
+
This plugin requires the `work` plugin to be enabled. Project tools are co-located in the work MCP server — same data domain, same Neo4j connection. If work tools are unavailable (work plugin not enabled), tell the user: "I need the work plugin to run structured projects. Would you like to enable it?" Do not attempt structured execution without work tools — fall back to a regular conversational approach until the dependency is resolved.
|
|
@@ -24,7 +24,7 @@ Understand what needs doing. Confirm the deliverable, the deadline (if any), and
|
|
|
24
24
|
|
|
25
25
|
### Execute
|
|
26
26
|
|
|
27
|
-
Do the work. Track progress via task notes. Check `
|
|
27
|
+
Do the work. Track progress via task notes. Check `work-ready` if there are any dependencies. Mark the child task complete when done.
|
|
28
28
|
|
|
29
29
|
### Done
|
|
30
30
|
|
|
@@ -42,7 +42,7 @@ Check for prior learnings: call `memory-search` with a description of the projec
|
|
|
42
42
|
|
|
43
43
|
### Execute
|
|
44
44
|
|
|
45
|
-
Work through the child tasks in dependency order. Before starting each task, check `
|
|
45
|
+
Work through the child tasks in dependency order. Before starting each task, check `work-ready` to confirm no blockers or AFFECTS conflicts. Append progress notes as work progresses. If a blocker is discovered mid-execution, create a new child task for it and link via `BLOCKS`.
|
|
46
46
|
|
|
47
47
|
Record phase transitions: `[PROJECT:PHASE] Execute — starting {task name}`.
|
|
48
48
|
|
|
@@ -21,7 +21,7 @@ The briefing has four sections, in this fixed order. Sections with no content co
|
|
|
21
21
|
|
|
22
22
|
### 1. Outstanding commitments
|
|
23
23
|
|
|
24
|
-
Call `
|
|
24
|
+
Call `work-list` filtered to `status: open` AND (`dueDate <= today` OR `priority: high`). Order by due date ascending, then by priority. List the top five with one line each: title, due date, related entity. If more than five qualify, end the list with "+ N more open" and the total count.
|
|
25
25
|
|
|
26
26
|
### 2. Today's calendar
|
|
27
27
|
|
|
@@ -63,7 +63,7 @@ The whole briefing must fit in one screen. If any section would overflow, collap
|
|
|
63
63
|
|
|
64
64
|
## Failure modes
|
|
65
65
|
|
|
66
|
-
- **`
|
|
66
|
+
- **`work-list` empty.** Section 1 collapses to "nothing outstanding".
|
|
67
67
|
- **`schedule-list` empty.** Section 2 collapses to "nothing in the diary today".
|
|
68
68
|
- **`memory-rank` returns fewer than five.** List what it returned; do not pad.
|
|
69
69
|
- **`profile-read` shows the owner has no `lastActiveSession`.** Section 4 collapses to "no prior session recorded" rather than reporting from training data.
|