overmind-mcp 2.2.4 → 2.3.0
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/.mcp.json.example +21 -0
- package/README.md +59 -6
- package/bin/.gitkeep +0 -0
- package/bin/README.md +34 -0
- package/dist/lib/InstallHelper.js +1 -1
- package/dist/lib/processRegistry.d.ts +71 -0
- package/dist/lib/processRegistry.d.ts.map +1 -0
- package/dist/lib/processRegistry.js +275 -0
- package/dist/lib/processRegistry.js.map +1 -0
- package/dist/server.d.ts +14 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +58 -1
- package/dist/server.js.map +1 -1
- package/dist/services/ClaudeRunner.d.ts.map +1 -1
- package/dist/services/ClaudeRunner.js +59 -4
- package/dist/services/ClaudeRunner.js.map +1 -1
- package/dist/services/ClineRunner.d.ts.map +1 -1
- package/dist/services/ClineRunner.js +12 -1
- package/dist/services/ClineRunner.js.map +1 -1
- package/dist/services/GeminiRunner.d.ts.map +1 -1
- package/dist/services/GeminiRunner.js +18 -0
- package/dist/services/GeminiRunner.js.map +1 -1
- package/dist/services/KiloRunner.d.ts.map +1 -1
- package/dist/services/KiloRunner.js +28 -1
- package/dist/services/KiloRunner.js.map +1 -1
- package/dist/services/NousHermesRunner.d.ts.map +1 -1
- package/dist/services/NousHermesRunner.js +18 -5
- package/dist/services/NousHermesRunner.js.map +1 -1
- package/dist/services/OpenClawRunner.d.ts.map +1 -1
- package/dist/services/OpenClawRunner.js +12 -2
- package/dist/services/OpenClawRunner.js.map +1 -1
- package/dist/services/OpenCodeRunner.d.ts.map +1 -1
- package/dist/services/OpenCodeRunner.js +12 -1
- package/dist/services/OpenCodeRunner.js.map +1 -1
- package/dist/services/QwenCliRunner.d.ts.map +1 -1
- package/dist/services/QwenCliRunner.js +12 -1
- package/dist/services/QwenCliRunner.js.map +1 -1
- package/dist/tools/config_example.d.ts.map +1 -1
- package/dist/tools/config_example.js +23 -7
- package/dist/tools/config_example.js.map +1 -1
- package/dist/tools/get_agent_status.d.ts +29 -0
- package/dist/tools/get_agent_status.d.ts.map +1 -0
- package/dist/tools/get_agent_status.js +45 -0
- package/dist/tools/get_agent_status.js.map +1 -0
- package/dist/tools/kill_agent.d.ts +22 -0
- package/dist/tools/kill_agent.d.ts.map +1 -0
- package/dist/tools/kill_agent.js +33 -0
- package/dist/tools/kill_agent.js.map +1 -0
- package/dist/tools/stream_agent_output.d.ts +30 -0
- package/dist/tools/stream_agent_output.d.ts.map +1 -0
- package/dist/tools/stream_agent_output.js +44 -0
- package/dist/tools/stream_agent_output.js.map +1 -0
- package/dist/tools/wait_agent.d.ts +30 -0
- package/dist/tools/wait_agent.d.ts.map +1 -0
- package/dist/tools/wait_agent.js +68 -0
- package/dist/tools/wait_agent.js.map +1 -0
- package/docs/ASYNC_AGENT_INTEGRATION.md +311 -0
- package/docs/INDEX.md +144 -144
- package/docs/docs/OVERMIND_WORKFLOW_GUIDE.md +595 -0
- package/docs/docs/PROJECT_STRUCTURE.md +101 -0
- package/docs/docs/README_POSTGRES_INTEGRATION.md +229 -0
- package/package.json +5 -5
- package/scripts/auto-install.mjs +8 -8
- package/scripts/postgres-manager.mjs +1 -1
- package/scripts/postinstall.mjs +2 -2
- package/scripts/setup.mjs +8 -5
- package/docs/guides/DEPLOYMENT.md +0 -418
- package/docs/guides/SWARM_USAGE.md +0 -444
- /package/{install-overmind-unix.sh → bin/install-overmind-unix.sh} +0 -0
- /package/{install-overmind-windows.bat → bin/install-overmind-windows.bat} +0 -0
- /package/docs/{api/prompt → prompt}/Claude_code.md +0 -0
- /package/docs/{api/prompt → prompt}/Kilo.md +0 -0
- /package/docs/{api/prompt → prompt}/Kilo_Hermes.md +0 -0
- /package/docs/{api/prompt → prompt}/Minimax4.md +0 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const streamAgentOutputSchema: z.ZodObject<{
|
|
3
|
+
agentName: z.ZodString;
|
|
4
|
+
runner: z.ZodOptional<z.ZodEnum<{
|
|
5
|
+
claude: "claude";
|
|
6
|
+
gemini: "gemini";
|
|
7
|
+
kilo: "kilo";
|
|
8
|
+
qwencli: "qwencli";
|
|
9
|
+
hermes: "hermes";
|
|
10
|
+
openclaw: "openclaw";
|
|
11
|
+
cline: "cline";
|
|
12
|
+
opencode: "opencode";
|
|
13
|
+
}>>;
|
|
14
|
+
sinceTimestamp: z.ZodOptional<z.ZodNumber>;
|
|
15
|
+
config: z.ZodOptional<z.ZodString>;
|
|
16
|
+
}, z.core.$strip>;
|
|
17
|
+
export declare function streamAgentOutputTool(args: z.infer<typeof streamAgentOutputSchema>): Promise<{
|
|
18
|
+
content: {
|
|
19
|
+
type: "text";
|
|
20
|
+
text: string;
|
|
21
|
+
}[];
|
|
22
|
+
isError?: undefined;
|
|
23
|
+
} | {
|
|
24
|
+
content: {
|
|
25
|
+
type: "text";
|
|
26
|
+
text: string;
|
|
27
|
+
}[];
|
|
28
|
+
isError: boolean;
|
|
29
|
+
}>;
|
|
30
|
+
//# sourceMappingURL=stream_agent_output.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream_agent_output.d.ts","sourceRoot":"","sources":["../../src/tools/stream_agent_output.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;iBAWlC,CAAC;AAEH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC;;;;;;;;;;;;GAiCxF"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { getProcessStatus } from '../lib/processRegistry.js';
|
|
3
|
+
export const streamAgentOutputSchema = z.object({
|
|
4
|
+
agentName: z.string().describe('Nom de l agent'),
|
|
5
|
+
runner: z
|
|
6
|
+
.enum(['claude', 'gemini', 'kilo', 'qwencli', 'openclaw', 'cline', 'opencode', 'hermes'])
|
|
7
|
+
.optional()
|
|
8
|
+
.describe('Type de runner (optionnel, défaut: any)'),
|
|
9
|
+
sinceTimestamp: z
|
|
10
|
+
.number()
|
|
11
|
+
.optional()
|
|
12
|
+
.describe('Ne retourner que la sortie après ce timestamp (ms)'),
|
|
13
|
+
config: z.string().optional().describe('Chemin du fichier de configuration'),
|
|
14
|
+
});
|
|
15
|
+
export async function streamAgentOutputTool(args) {
|
|
16
|
+
const { agentName, runner, sinceTimestamp, config: configPath } = args;
|
|
17
|
+
const entry = await getProcessStatus(agentName, runner, configPath);
|
|
18
|
+
if (!entry) {
|
|
19
|
+
return {
|
|
20
|
+
content: [
|
|
21
|
+
{
|
|
22
|
+
type: 'text',
|
|
23
|
+
text: `Agent "${agentName}" non trouvé dans le registre.`,
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
const isComplete = entry.status === 'done' || entry.status === 'failed' || entry.status === 'orphaned';
|
|
29
|
+
const output = entry.outputBuffer || '';
|
|
30
|
+
if (sinceTimestamp && entry.lastOutputAt && entry.lastOutputAt > sinceTimestamp) {
|
|
31
|
+
// For now, return all output if there was output after the timestamp
|
|
32
|
+
// (per-chunk timestamps not yet implemented)
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
content: [
|
|
36
|
+
{
|
|
37
|
+
type: 'text',
|
|
38
|
+
text: output || '(no output yet)',
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
isError: isComplete && entry.status === 'failed',
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=stream_agent_output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream_agent_output.js","sourceRoot":"","sources":["../../src/tools/stream_agent_output.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAE7D,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,gBAAgB,CAAC;IAChD,MAAM,EAAE,CAAC;SACN,IAAI,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC;SACxF,QAAQ,EAAE;SACV,QAAQ,CAAC,yCAAyC,CAAC;IACtD,cAAc,EAAE,CAAC;SACd,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CAAC,oDAAoD,CAAC;IACjE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,oCAAoC,CAAC;CAC7E,CAAC,CAAC;AAEH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,IAA6C;IACvF,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC;IAEvE,MAAM,KAAK,GAAG,MAAM,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;IAEpE,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAe;oBACrB,IAAI,EAAE,UAAU,SAAS,gCAAgC;iBAC1D;aACF;SACF,CAAC;IACJ,CAAC;IAED,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,KAAK,MAAM,IAAI,KAAK,CAAC,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,UAAU,CAAC;IACvG,MAAM,MAAM,GAAG,KAAK,CAAC,YAAY,IAAI,EAAE,CAAC;IAExC,IAAI,cAAc,IAAI,KAAK,CAAC,YAAY,IAAI,KAAK,CAAC,YAAY,GAAG,cAAc,EAAE,CAAC;QAChF,qEAAqE;QACrE,6CAA6C;IAC/C,CAAC;IAED,OAAO;QACL,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,MAAe;gBACrB,IAAI,EAAE,MAAM,IAAI,iBAAiB;aAClC;SACF;QACD,OAAO,EAAE,UAAU,IAAI,KAAK,CAAC,MAAM,KAAK,QAAQ;KACjD,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const waitAgentSchema: z.ZodObject<{
|
|
3
|
+
agentName: z.ZodString;
|
|
4
|
+
runner: z.ZodOptional<z.ZodEnum<{
|
|
5
|
+
claude: "claude";
|
|
6
|
+
gemini: "gemini";
|
|
7
|
+
kilo: "kilo";
|
|
8
|
+
qwencli: "qwencli";
|
|
9
|
+
hermes: "hermes";
|
|
10
|
+
openclaw: "openclaw";
|
|
11
|
+
cline: "cline";
|
|
12
|
+
opencode: "opencode";
|
|
13
|
+
}>>;
|
|
14
|
+
timeoutMs: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
15
|
+
config: z.ZodOptional<z.ZodString>;
|
|
16
|
+
}, z.core.$strip>;
|
|
17
|
+
export declare function waitAgentTool(args: z.infer<typeof waitAgentSchema>): Promise<{
|
|
18
|
+
content: {
|
|
19
|
+
type: "text";
|
|
20
|
+
text: string;
|
|
21
|
+
}[];
|
|
22
|
+
isError: boolean;
|
|
23
|
+
} | {
|
|
24
|
+
content: {
|
|
25
|
+
type: "text";
|
|
26
|
+
text: string;
|
|
27
|
+
}[];
|
|
28
|
+
isError?: undefined;
|
|
29
|
+
}>;
|
|
30
|
+
//# sourceMappingURL=wait_agent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wait_agent.d.ts","sourceRoot":"","sources":["../../src/tools/wait_agent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,eAAO,MAAM,eAAe;;;;;;;;;;;;;;iBAY1B,CAAC;AAEH,wBAAsB,aAAa,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC;;;;;;;;;;;;GA0DxE"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { getProcessStatus } from '../lib/processRegistry.js';
|
|
3
|
+
export const waitAgentSchema = z.object({
|
|
4
|
+
agentName: z.string().describe('Nom de l agent'),
|
|
5
|
+
runner: z
|
|
6
|
+
.enum(['claude', 'gemini', 'kilo', 'qwencli', 'openclaw', 'cline', 'opencode', 'hermes'])
|
|
7
|
+
.optional()
|
|
8
|
+
.describe('Type de runner (optionnel, défaut: any)'),
|
|
9
|
+
timeoutMs: z
|
|
10
|
+
.number()
|
|
11
|
+
.optional()
|
|
12
|
+
.default(900000)
|
|
13
|
+
.describe('Timeout en ms (défaut: 900000 = 15 min)'),
|
|
14
|
+
config: z.string().optional().describe('Chemin du fichier de configuration'),
|
|
15
|
+
});
|
|
16
|
+
export async function waitAgentTool(args) {
|
|
17
|
+
const { agentName, runner, timeoutMs, config: configPath } = args;
|
|
18
|
+
const start = Date.now();
|
|
19
|
+
const pollInterval = 1000; // 1 second
|
|
20
|
+
while (Date.now() - start < timeoutMs) {
|
|
21
|
+
const entry = await getProcessStatus(agentName, runner, configPath);
|
|
22
|
+
if (!entry) {
|
|
23
|
+
return {
|
|
24
|
+
content: [
|
|
25
|
+
{
|
|
26
|
+
type: 'text',
|
|
27
|
+
text: `Agent "${agentName}" n existe plus dans le registre.`,
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
isError: true,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
if (entry.status === 'done') {
|
|
34
|
+
return {
|
|
35
|
+
content: [
|
|
36
|
+
{
|
|
37
|
+
type: 'text',
|
|
38
|
+
text: entry.outputBuffer || 'Agent terminé avec succès.',
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
if (entry.status === 'failed' || entry.status === 'orphaned') {
|
|
44
|
+
return {
|
|
45
|
+
content: [
|
|
46
|
+
{
|
|
47
|
+
type: 'text',
|
|
48
|
+
text: `Agent terminé avec erreur: ${entry.status}\n\nSortie:\n${entry.outputBuffer || 'N/A'}`,
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
isError: true,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
// Still running — wait before next poll
|
|
55
|
+
await new Promise((r) => setTimeout(r, pollInterval));
|
|
56
|
+
}
|
|
57
|
+
// Timeout reached
|
|
58
|
+
return {
|
|
59
|
+
content: [
|
|
60
|
+
{
|
|
61
|
+
type: 'text',
|
|
62
|
+
text: `Timeout de ${timeoutMs}ms atteint. L'agent est toujours en cours d exécution.`,
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
isError: true,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=wait_agent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wait_agent.js","sourceRoot":"","sources":["../../src/tools/wait_agent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAE7D,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,gBAAgB,CAAC;IAChD,MAAM,EAAE,CAAC;SACN,IAAI,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC;SACxF,QAAQ,EAAE;SACV,QAAQ,CAAC,yCAAyC,CAAC;IACtD,SAAS,EAAE,CAAC;SACT,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,OAAO,CAAC,MAAM,CAAC;SACf,QAAQ,CAAC,yCAAyC,CAAC;IACtD,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,oCAAoC,CAAC;CAC7E,CAAC,CAAC;AAEH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAqC;IACvE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC;IAElE,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACzB,MAAM,YAAY,GAAG,IAAI,CAAC,CAAC,WAAW;IAEtC,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,GAAG,SAAS,EAAE,CAAC;QACtC,MAAM,KAAK,GAAG,MAAM,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;QAEpE,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,UAAU,SAAS,mCAAmC;qBAC7D;iBACF;gBACD,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;QAED,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;YAC5B,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,KAAK,CAAC,YAAY,IAAI,4BAA4B;qBACzD;iBACF;aACF,CAAC;QACJ,CAAC;QAED,IAAI,KAAK,CAAC,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;YAC7D,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,8BAA8B,KAAK,CAAC,MAAM,gBAAgB,KAAK,CAAC,YAAY,IAAI,KAAK,EAAE;qBAC9F;iBACF;gBACD,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;QAED,wCAAwC;QACxC,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC;IACxD,CAAC;IAED,kBAAkB;IAClB,OAAO;QACL,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,MAAe;gBACrB,IAAI,EAAE,cAAc,SAAS,wDAAwD;aACtF;SACF;QACD,OAAO,EAAE,IAAI;KACd,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
# OverMind Async Agent Integration — Process Registry & PID Tracking
|
|
2
|
+
|
|
3
|
+
> **Problème résolu** : Les agents IA sont invoked en async via MCP, mais le seul lien avec le processus fils est le `sessionId` (généré par le runner, opaque pour OverMind). Si le processus parent meurt, le `sessionId` est perdu et le child process devient orphelin.
|
|
4
|
+
>
|
|
5
|
+
> **Solution** : Un **Process Registry** qui stocke le mapping `pid ↔ sessionId ↔ agentName`, persistant dans `sessions.json`, permettant de :
|
|
6
|
+
> - Se rattacher à un agent en cours via son PID
|
|
7
|
+
> - Vérifier si un agent est encore vivant
|
|
8
|
+
> - Tuer un agent par son PID
|
|
9
|
+
> - Streamer output en temps réel
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 1. Architecture
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
┌──────────────────────────────────────────────────────────────┐
|
|
17
|
+
│ MCP Tool: run_agent() (async, non-blocking) │
|
|
18
|
+
│ │
|
|
19
|
+
│ 1. Spawn child process (claude/kilo/gemini/etc.) │
|
|
20
|
+
│ 2. Register { pid, sessionId, agentName, runner, ts } │
|
|
21
|
+
│ 3. Return immediately with { sessionId, pid } │
|
|
22
|
+
│ → Client can poll / attach to PID │
|
|
23
|
+
│ │
|
|
24
|
+
│ ┌──────────────────┐ ┌──────────────────────────────┐ │
|
|
25
|
+
│ │ Process Registry │◄──│ child.on('data') │ │
|
|
26
|
+
│ │ (sessions.json) │ │ → buffers output │ │
|
|
27
|
+
│ │ │ │ → checks liveliness │ │
|
|
28
|
+
│ │ { pid, sessionId,│ └──────────────────────────────┘ │
|
|
29
|
+
│ │ agentName, │ │
|
|
30
|
+
│ │ runner, status,│ ┌──────────────────────────────┐ │
|
|
31
|
+
│ │ startedAt } │ │ MCP Tool: get_agent_status() │ │
|
|
32
|
+
│ └──────────────────┘ │ → returns live output │ │
|
|
33
|
+
│ │ → pid / alive / output so far │ │
|
|
34
|
+
│ └──────────────────────────────┘ │
|
|
35
|
+
└──────────────────────────────────────────────────────────────┘
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 2. Sessions JSON — Nouveau Format
|
|
41
|
+
|
|
42
|
+
**Avant** (sessions.json) :
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"kilo:sniper_analyst": { "id": "sess_abc123", "ts": 1746892800000 }
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Après** (sessions.json + process registry) :
|
|
50
|
+
```json
|
|
51
|
+
{
|
|
52
|
+
"kilo:sniper_analyst": {
|
|
53
|
+
"id": "sess_abc123",
|
|
54
|
+
"ts": 1746892800000,
|
|
55
|
+
"pid": 12345,
|
|
56
|
+
"status": "running",
|
|
57
|
+
"outputBuffer": ""
|
|
58
|
+
},
|
|
59
|
+
"claude:planner": {
|
|
60
|
+
"id": "sess_def456",
|
|
61
|
+
"ts": 1746892900000,
|
|
62
|
+
"pid": 67890,
|
|
63
|
+
"status": "running",
|
|
64
|
+
"outputBuffer": "Thinking...\n"
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Le champ `status` peut être :
|
|
70
|
+
- `running` — processus actif, PID valide
|
|
71
|
+
- `done` — terminé avec succès (garde le last output pour retrieval)
|
|
72
|
+
- `failed` — terminé avec erreur
|
|
73
|
+
- `orphaned` — le parent a crash mais le child tourne encore
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## 3. Runner Changes — Spawn & Register
|
|
78
|
+
|
|
79
|
+
Chaque runner (Claude, Kilo, Gemini, Hermes, etc.) doit :
|
|
80
|
+
|
|
81
|
+
1. **Stocker le PID** dès le `spawn()` :
|
|
82
|
+
```typescript
|
|
83
|
+
const child = spawn(command, args, options);
|
|
84
|
+
// Immediately register
|
|
85
|
+
await registerProcess(child.pid, {
|
|
86
|
+
sessionId: undefined, // filled when runner gives us sessionId
|
|
87
|
+
agentName,
|
|
88
|
+
runner,
|
|
89
|
+
startedAt: Date.now(),
|
|
90
|
+
status: 'running',
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
2. **Mettre à jour avec le sessionId** dès qu'il est reçu :
|
|
95
|
+
```typescript
|
|
96
|
+
child.stdout?.on('data', (d) => {
|
|
97
|
+
const chunk = d.toString();
|
|
98
|
+
outputBuffer += chunk;
|
|
99
|
+
// Si le sessionId arrive pour la première fois
|
|
100
|
+
if (sessionId && !currentSessionId) {
|
|
101
|
+
currentSessionId = sessionId;
|
|
102
|
+
await updateProcessSession(sessionId, child.pid);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
3. **Marquer `done/failed`** à `child.on('close')` :
|
|
108
|
+
```typescript
|
|
109
|
+
child.on('close', async (code) => {
|
|
110
|
+
await updateProcessStatus(child.pid, code === 0 ? 'done' : 'failed');
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
4. **Cleanup à la terminaison** :
|
|
115
|
+
```typescript
|
|
116
|
+
// Supprimer après 1h ( TTL ) ou sur explicit delete
|
|
117
|
+
await unregisterProcess(pid);
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## 4. Nouvelles Fonctions Registry
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
// ─── Register a new running process ───────────────────────────
|
|
126
|
+
/**
|
|
127
|
+
* Called immediately after spawn(). Records the PID before the sessionId
|
|
128
|
+
* is known (sessionId arrives later from stdout).
|
|
129
|
+
*/
|
|
130
|
+
export async function registerProcess(
|
|
131
|
+
pid: number,
|
|
132
|
+
meta: {
|
|
133
|
+
agentName: string;
|
|
134
|
+
runner: string;
|
|
135
|
+
startedAt: number;
|
|
136
|
+
configPath?: string;
|
|
137
|
+
},
|
|
138
|
+
): Promise<void> { ... }
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Called when the runner emits a sessionId for the first time.
|
|
142
|
+
* Links sessionId ↔ pid in the registry.
|
|
143
|
+
*/
|
|
144
|
+
export async function linkSessionToPid(
|
|
145
|
+
sessionId: string,
|
|
146
|
+
pid: number,
|
|
147
|
+
configPath?: string,
|
|
148
|
+
): Promise<void> { ... }
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Update output buffer for live streaming.
|
|
152
|
+
*/
|
|
153
|
+
export async function appendOutput(
|
|
154
|
+
pid: number,
|
|
155
|
+
chunk: string,
|
|
156
|
+
configPath?: string,
|
|
157
|
+
): Promise<void> { ... }
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Get current status + output buffer for a process.
|
|
161
|
+
*/
|
|
162
|
+
export async function getProcessStatus(
|
|
163
|
+
agentName: string,
|
|
164
|
+
runner?: string,
|
|
165
|
+
configPath?: string,
|
|
166
|
+
): Promise<ProcessStatus | null> { ... }
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Kill a running agent by PID (Windows: taskkill /F /T /PID; Unix: SIGKILL).
|
|
170
|
+
*/
|
|
171
|
+
export async function killAgent(
|
|
172
|
+
agentName: string,
|
|
173
|
+
runner?: string,
|
|
174
|
+
configPath?: string,
|
|
175
|
+
): Promise<boolean> { ... }
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Unregister (cleanup) a process entry.
|
|
179
|
+
*/
|
|
180
|
+
export async function unregisterProcess(
|
|
181
|
+
pid: number,
|
|
182
|
+
configPath?: string,
|
|
183
|
+
): Promise<void> { ... }
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## 5. TTL & Cleanup
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
const PROCESS_TTL_MS = 60 * 60 * 1000; // 1 hour after 'done'/'failed'
|
|
192
|
+
const ORPHAN_CHECK_INTERVAL = 5 * 60 * 1000; // 5 minutes
|
|
193
|
+
|
|
194
|
+
// On startup, scan for:
|
|
195
|
+
// 1. 'running' entries where the PID no longer exists → mark 'orphaned'
|
|
196
|
+
// 2. 'done'/'failed' entries older than PROCESS_TTL_MS → unregister
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## 6. Nouveaux Outils MCP
|
|
202
|
+
|
|
203
|
+
### `get_agent_status`
|
|
204
|
+
```typescript
|
|
205
|
+
{
|
|
206
|
+
agentName: string;
|
|
207
|
+
runner?: string; // defaults to 'claude' if omitted
|
|
208
|
+
}
|
|
209
|
+
// Returns:
|
|
210
|
+
// { status, pid, outputBuffer, startedAt, sessionId }
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### `stream_agent_output`
|
|
214
|
+
```typescript
|
|
215
|
+
{
|
|
216
|
+
agentName: string;
|
|
217
|
+
runner?: string;
|
|
218
|
+
sinceTimestamp?: number; // only return output after this ts
|
|
219
|
+
}
|
|
220
|
+
// Returns:
|
|
221
|
+
// { output: string, status, isComplete: boolean }
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### `kill_agent`
|
|
225
|
+
```typescript
|
|
226
|
+
{
|
|
227
|
+
agentName: string;
|
|
228
|
+
runner?: string;
|
|
229
|
+
}
|
|
230
|
+
// Returns:
|
|
231
|
+
// { killed: boolean, pid: number }
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### `wait_agent`
|
|
235
|
+
```typescript
|
|
236
|
+
{
|
|
237
|
+
agentName: string;
|
|
238
|
+
runner?: string;
|
|
239
|
+
timeoutMs?: number; // default: 900000 (15 min)
|
|
240
|
+
}
|
|
241
|
+
// Polls until status !== 'running', returns final result
|
|
242
|
+
// Returns:
|
|
243
|
+
// { status, result, exitCode }
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## 7. Flux Complet — Async Agent Lifecycle
|
|
249
|
+
|
|
250
|
+
```
|
|
251
|
+
Client OverMind MCP Runner
|
|
252
|
+
│ │ │
|
|
253
|
+
│ run_agent() │ │
|
|
254
|
+
│─────────────────────────►│ │
|
|
255
|
+
│ │ spawn(child) │
|
|
256
|
+
│ │───────────────────────────►│
|
|
257
|
+
│ │ registerProcess(pid) │
|
|
258
|
+
│ │ Return { sessionId, pid } │
|
|
259
|
+
│ { sessionId, pid } │ │
|
|
260
|
+
│◄──────────────────────────│ │
|
|
261
|
+
│ │ │
|
|
262
|
+
│ get_agent_status() │ │
|
|
263
|
+
│─────────────────────────►│ │
|
|
264
|
+
│ │ getProcessStatus() │
|
|
265
|
+
│ { status, outputBuffer }│ │
|
|
266
|
+
│◄──────────────────────────│ │
|
|
267
|
+
│ │ stdout.on('data') │
|
|
268
|
+
│ │◄──────────────────────────│
|
|
269
|
+
│ │ appendOutput(pid, chunk) │
|
|
270
|
+
│ │ │
|
|
271
|
+
│ stream_agent_output() │ │
|
|
272
|
+
│─────────────────────────►│ │
|
|
273
|
+
│ { output, status } │ │
|
|
274
|
+
│◄──────────────────────────│ │
|
|
275
|
+
│ │ child.on('close') │
|
|
276
|
+
│ │◄──────────────────────────│
|
|
277
|
+
│ │ updateProcessStatus(done) │
|
|
278
|
+
│ │ │
|
|
279
|
+
│ wait_agent() │ │
|
|
280
|
+
│─────────────────────────►│ │
|
|
281
|
+
│ Polls until done │ │
|
|
282
|
+
│ { status, result } │ │
|
|
283
|
+
│◄──────────────────────────│ │
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## 8. Backward Compatibility
|
|
289
|
+
|
|
290
|
+
- Les champs existants (`id`, `ts`) dans `sessions.json` ne changent pas
|
|
291
|
+
- `status`, `pid`, `outputBuffer` sont **ajoutés** (optionnels)
|
|
292
|
+
- Le `sessionId` reste le même — les outils existants (`autoResume`) continuent de fonctionner
|
|
293
|
+
- Si `pid` n'existe pas dans une entrée (sessions anciennes), le status est déduit de `ts` :
|
|
294
|
+
- `ts` < 30 jours + pas de `pid` → `done` (legacy)
|
|
295
|
+
- `ts` récent + pas de `pid` → `running` (legacy, mais incertain)
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
## 9. Implémentation Minimale — Checklist
|
|
300
|
+
|
|
301
|
+
- [ ] `src/lib/processRegistry.ts` — nouvelles fonctions (register, link, append, status, kill, unregister)
|
|
302
|
+
- [ ] `src/lib/sessions.ts` — ajout champs `pid`, `status`, `outputBuffer`
|
|
303
|
+
- [ ] Chaque runner (8 fichiers) — ajouter `registerProcess()` après `spawn()`
|
|
304
|
+
- [ ] Chaque runner — ajouter `appendOutput()` dans `stdout.on('data')`
|
|
305
|
+
- [ ] Chaque runner — ajouter `updateProcessStatus()` dans `child.on('close')`
|
|
306
|
+
- [ ] `src/tools/get_agent_status.ts` — nouvel outil MCP
|
|
307
|
+
- [ ] `src/tools/stream_agent_output.ts` — nouvel outil MCP
|
|
308
|
+
- [ ] `src/tools/kill_agent.ts` — nouvel outil MCP
|
|
309
|
+
- [ ] `src/tools/wait_agent.ts` — nouvel outil MCP
|
|
310
|
+
- [ ] `server.ts` — register les 4 nouveaux outils
|
|
311
|
+
- [ ] Tests dans `src/__tests__/processRegistry.test.ts`
|